How it works
This API processes customer service emails and interacts with NetSuite. A classifier analyzes incoming emails and outputs structured JSON, which this API uses to query or write data to NetSuite via Claude AI agents.
Built by @ishan_lakh
End-to-End Flow
1. Email Classification
The classifier prompt analyzes emails and extracts:
- Customer identity (email, name, company)
- Questions grouped by workflow type
- PDF/attachment data (PO items, SKUs, quantities)
- Boolean flags for which workflows to run
2. Workflow Routing
Based on classifier output, the API routes to appropriate workflows:
| Flag | Trigger | Action |
|---|---|---|
| has_po2so: true | CSR says "create so from po" | ā ļø Creates Sales Order in NetSuite |
| has_stock_price: true | Customer asks about pricing/inventory | Queries items, prices, availability |
| has_ship_date: true | Customer asks about order status | Queries orders, ship dates, fulfillments |
| has_other: true | General NetSuite-answerable question | Queries account data, balances, history |
3. NetSuite Interaction
Claude AI agents execute SuiteQL queries and return structured data. For po2so workflow, agents can also create records.
Expected Input Format
The classifier produces this JSON structure, which becomes the API request body:
{
"customer_email": "buyer@acmecorp.com",
"customer_name": "Jane Doe",
"customer_company": "ACME Corp",
"has_po2so": false,
"has_stock_price": true,
"has_ship_date": false,
"has_hold": false,
"has_other": false,
"po2so_questions": [],
"stock_price_questions": [
{
"index": 0,
"normalized_question": "What is the price for item SKU-001?",
"source_quote": "can you send pricing for SKU-001?",
"item_hints": ["SKU-001"],
"po_number": null,
"so_number": null
}
],
"ship_date_questions": [],
"hold_questions": [],
"other_questions": [],
"pdf_data": {
"document_type": "Purchase Order",
"po_number": "PO-2025-001",
"vendor": "ACME Corp",
"items": [
{
"line": 1,
"sku": "SKU-001",
"description": "Widget A ā Gold ā 1 Inch",
"quantity": 100,
"unit_price": 10.50
}
],
"total": 1050.00,
"notes": "Rush order"
}
}PO to SO Conversion
ā ļø This creates real records in NetSuite. The po2so workflow only triggers when an internal CSR explicitly requests it with phrases like "create so from po". Customer emails alone will NOT trigger this.
Trigger Phrases (from internal CSR)
ā "create so from po" ā "convert this po to so" ā "enter this po as a sales order" ā "create sales order from this purchase order" ā Customer: "Please confirm the attached PO" ā routes to ship_date ā Customer: "Here is our order" ā routes to ship_date
What Happens
- Classifier sets
has_po2so: true - API runs PO2SO preview (matches PDF items to NetSuite items)
- If items matched successfully, creates the Sales Order
- Returns
so_createdwith SO number
Response when SO is created
{
"workflow": "po2so",
"raw_data": {
"customer": { "id": 12345, "name": "ACME Corp" },
"so_created": {
"id": 67890,
"tranid": "SOSX25-12345"
},
"po_items": [
{
"original_sku": "SKU-001",
"matched_item": { "itemid": "SKU-001-GOLD", "unit_price": 10.50 },
"match_status": "matched"
}
]
},
"summary": "Sales Order SOSX25-12345 created from PO PO-2025-001"
}Endpoints
Main endpoint. Accepts classifier JSON output and routes to appropriate workflows. Handles all workflow types including po2so (which can write to NetSuite).
Direct PO to SO endpoint. Use create_so: true to create, create_so: false for preview only.
All Input Fields
| Field | Type | Description |
|---|---|---|
| auth_keyrequired | string | API authentication key |
| customer_email | string | Customer's email address |
| customer_name | string | Contact name (not used for lookup) |
| customer_company | string | Company name for NetSuite customer lookup |
| has_po2so | boolean | ā ļø When true, creates Sales Order |
| has_stock_price | boolean | Triggers stock/price workflow |
| has_ship_date | boolean | Triggers ship date workflow |
| has_hold | boolean | Triggers hold status workflow |
| has_other | boolean | Triggers general query workflow |
| po2so_questions | array | PO to SO requests (needs CSR trigger phrase) |
| stock_price_questions | array | Pricing and inventory questions |
| ship_date_questions | array | Order status and shipping questions |
| hold_questions | array | Account hold status questions |
| other_questions | array | General account inquiries |
| pdf_data | object | Extracted PO/catalog data with items array |
Question Object Format
Each question in the arrays follows this structure:
{
"index": 0,
"normalized_question": "What is the price for SKU-001?",
"source_quote": "can you send pricing for SKU-001?",
"item_hints": ["SKU-001", "Widget"],
"po_number": "PO-2025-001",
"so_number": null
}pdf_data Object Format
{
"document_type": "Purchase Order",
"po_number": "PO-2025-001",
"vendor": "ACME Corp",
"items": [
{
"line": 1,
"sku": "SKU-001",
"description": "Widget A ā Gold ā 1 Inch",
"quantity": 100,
"unit_price": 10.50
}
],
"total": 1050.00,
"notes": "Rush order, ship by end of month"
}Tip: The unit_price in pdf_data helps match items to customer's purchase history. Items with matching prices are prioritized.
API Response Format
{
"ok": true,
"customer_identity": {
"email": "buyer@acmecorp.com",
"name": "Jane Doe",
"company": "ACME Corp"
},
"workflow_results": [
{
"workflow": "stock_price",
"raw_data": {
"customer": { "id": 12345, "name": "ACME Corporation" },
"items": [
{
"itemid": "SKU-001-GOLD",
"description": "Widget A Gold",
"unit_price": 10.50,
"on_hand": 500,
"available": 450,
"committed": 50
}
],
"notes": ["Found in customer history at $10.50"]
},
"summary": "Found 1 item(s)",
"errors": []
}
],
"debug": {
"request_id": "req_abc123",
"model_used": "claude-3-5-haiku-20241022",
"timings": { "stock_price": 5000, "total": 5000 }
}
}Error Codes
| Status | Description |
|---|---|
| 401 | Invalid or missing auth_key |
| 400 | Invalid request body or no questions provided |
| 500 | Internal server error or NetSuite connection failed |
NetSuite MCP API v1.0