Salesforce Order Management for orders, returns, and cases via Cirra AI MCP Server. Use when asked about order status, return orders, return labels, support cases from returns, or the order-to-return-to-case lifecycle. Usage: /sf-orders [order|return|case] {details} ...
Manage orders, returns, and support cases in Salesforce using the Cirra AI MCP Server. Covers the full order-to-return-to-case lifecycle using standard Salesforce objects (Order, ReturnOrder, ReturnOrderLineItem, Case).
Parse $ARGUMENTS to determine the operation:
| First argument or intent | Workflow |
|---|---|
order, order status, order details | Order Status |
return, return order, RMA, return label | Return Management |
case, support case, case from return | Case Management |
| (no argument or unclear) | Ask the user (see below) |
When the operation is missing or unclear, you MUST use AskUserQuestion before proceeding:
AskUserQuestion(question="What would you like to do?\n\n1. **Order** — query order status and details\n2. **Return** — create a return order or send a return label\n3. **Case** — create or update a support case")
Do NOT guess the operation or default to one. Wait for the user's answer.
FIRST: Call cirra_ai_init() with no parameters:
cirra_ai_init()
"I've connected to [org]. Would you like me to use the defaults, or do you want to select different options?"
Do not ask for org details before calling cirra_ai_init().
THEN: Detect available objects and packages.
Use sobject_describe on each required object to verify it exists. Call sobject_describe for Order, ReturnOrder, and Case. If the call succeeds, the object is available; if it returns an error (object not found), the object is not available in this org.
Set capability flags:
HAS_ORDERS = true if sobject_describe(sObject="Order") succeedsHAS_RETURNS = true if sobject_describe(sObject="ReturnOrder") succeedsHAS_CASES = true if sobject_describe(sObject="Case") succeeds (almost always true)If ReturnOrder is missing, inform the user that return operations are unavailable because Order Management / Service Cloud is not enabled. The skill can still handle order status checks and general case management.
Use sobject_describe on ReturnOrder and look for LabelEmailSent__c (Checkbox) and LabelEmailSentDate__c (DateTime).
HAS_LABEL_TRACKING = true if both fields existThen check for the return label flow using tooling_api_query:
sObject: FlowDefinition
whereClause: DeveloperName = 'Send_Return_Label_Email' AND ActiveVersionId != null
HAS_RETURN_LABEL_FLOW = true if the query returns a result with an active version
If the flow doesn't exist and the user needs email functionality, see references/metadata-setup.md.
| Operation | Requires |
|---|---|
| Check Order Status | HAS_ORDERS |
| Create Return | HAS_ORDERS + HAS_RETURNS |
| Email Return Label | HAS_RETURNS (flow preferred; Task fallback if no flow) |
| Update Case Status | HAS_CASES |
| Create Case from Return | HAS_RETURNS + HAS_CASES |
The Cirra AI MCP Server's soql_query tool requires structured parameters, not raw SOQL strings. Always provide:
sObject — the object API namefields — array of field names (relationship fields like Account.Name are supported)whereClause — the WHERE condition (without the WHERE keyword)orderBy — ORDER BY clause (pass empty string "" if not needed)groupBy — GROUP BY clause (pass empty string "" if not needed)limit — max records to returnThe tool does not support subqueries in the fields array. To get child records (e.g., OrderItems for an Order), run a separate query on the child object with a WHERE filter on the parent ID.
For DML operations, use sobject_dml with sObject, operation (insert/update/delete/upsert), and records array. For delete operations, use recordIds (string array of IDs) instead of records.
IMPORTANT — Resolved IDs: When a lookup query accepts both an ID and a human-readable number (e.g., Id OR OrderNumber), always use the Id returned from the query result for all subsequent operations (DML, child queries, updates). Never pass raw user input (which may be a number, not an ID) into DML or relationship queries.
IMPORTANT — Input format detection: Before constructing a whereClause that accepts user input, detect whether the input is a Salesforce ID (15 or 18 alphanumeric characters) or a human-readable number/string. Using Id = '{value}' OR SomeNumber = '{value}' causes a MALFORMED_ID error when the value is not a valid ID format because Salesforce validates the Id field before evaluating the OR. Instead:
Id = '{value}'OrderNumber = '{value}')When a user asks about an order's status, shipping, or details:
The user may provide a Salesforce ID (15/18 chars) or an OrderNumber. Detect the input format (see Tool Usage Notes) and use the appropriate whereClause.
Use soql_query:
Order["Id", "OrderNumber", "Status", "TotalAmount", "AccountId", "Account.Name", "EffectiveDate", "EndDate", "Description", "Type"]Id = '{orderId}' (if ID format) or OrderNumber = '{orderId}' (if number format)OrderNumber1Use the Order Id from the query result above (not the raw user input).
Use soql_query:
OrderItem["Id", "OrderId", "Product2.Name", "Quantity", "UnitPrice", "TotalPrice", "Description"]OrderId = '{order.Id}'CreatedDate ASC200link_buildWhen a user wants to return items from an order:
Gather requirements:
Step 1: Verify the order exists and get its details.
Use soql_query:
Order["Id", "OrderNumber", "Status", "AccountId", "Account.Name", "TotalAmount"]Id = '{orderId}' (if ID format) or OrderNumber = '{orderId}' (if number format)1Step 2: Get order line items (use the Order Id from Step 1, not raw user input).
Use soql_query:
OrderItem["Id", "OrderId", "Product2Id", "Product2.Name", "Quantity", "UnitPrice", "TotalPrice"]OrderId = '{order.Id}'CreatedDate ASC200If the user didn't specify a line item, present available items and ask which one(s) to return. If there's only one item, or the user says "auto-detect", use the first item.
Step 3: Validate return quantity — cannot exceed original order quantity.
Step 1: Create the ReturnOrder using sobject_dml:
ReturnOrderinsert[
{
"OrderId": "<original_order_id>",
"AccountId": "<account_id_from_order>",
"Status": "Draft",
"Description": "Return for Order Item <line_item_id> - <reason>"
}
]
Step 2: Create the ReturnOrderLineItem using sobject_dml:
ReturnOrderLineIteminsert[
{
"ReturnOrderId": "<return_order_id_from_step1>",
"OrderItemId": "<line_item_id>",
"Product2Id": "<product_id_from_order_item>",
"QuantityReturned": "<quantity>",
"Description": "Return <quantity> unit(s) - <reason>"
}
]
Step 3: Update ReturnOrder status to Submitted using sobject_dml:
ReturnOrderupdate[
{
"Id": "<return_order_id>",
"Status": "Submitted"
}
]
Rollback on failure: If ReturnOrderLineItem creation fails, delete the ReturnOrder:
ReturnOrderdelete["<return_order_id>"]Query the created return order using soql_query:
ReturnOrder["Id", "ReturnOrderNumber", "Status", "Description", "AccountId", "OrderId"]Id = '{return_order_id}'1Then query line items separately:
ReturnOrderLineItem["Id", "Product2.Name", "QuantityReturned", "Description"]ReturnOrderId = '{return_order_id}'200Present results with the return order number, status, and a link to the record.
When a user wants to send a return shipping label to a customer:
HAS_RETURNS must be trueHAS_LABEL_TRACKING = true, check that the label hasn't already been sentStep 1: Verify the return order.
Use soql_query:
ReturnOrder["Id", "ReturnOrderNumber", "OrderId", "Status", "Description"] (add LabelEmailSent__c, LabelEmailSentDate__c if HAS_LABEL_TRACKING = true)Id = '{returnOrderId}' (if ID format) or ReturnOrderNumber = '{returnOrderId}' (if number format)1Step 2: Business validation:
LabelEmailSent__c = true, reject: "Return label already sent on {date}"If HAS_RETURN_LABEL_FLOW = true, the flow should be triggered. Since the Cirra AI MCP doesn't have a direct "run flow" tool, guide the user to trigger the flow manually or use tooling_api_dml.
If HAS_LABEL_TRACKING = true and the flow was triggered successfully, update the tracking fields:
Use sobject_dml:
ReturnOrderupdate[
{
"Id": "{returnOrder.Id}",
"LabelEmailSent__c": true,
"LabelEmailSentDate__c": "<current_datetime_ISO>"
}
]
If HAS_RETURN_LABEL_FLOW = false, create a Task record as a fallback so the label can be sent manually. Do not update LabelEmailSent__c — no email has actually been sent yet.
Use the ReturnOrder Id and ReturnOrderNumber from the Step 1 query result for all fields below.
Use sobject_dml:
Taskinsert[
{
"Subject": "Send Return Label - {returnOrder.ReturnOrderNumber}",
"Description": "Send return shipping label to {customerEmail} for return order {returnOrder.ReturnOrderNumber}",
"Status": "Not Started",
"Priority": "High",
"WhatId": "{returnOrder.Id}",
"ActivityDate": "<today>"
}
]
When a user wants to update a support case:
Step 1: Verify the case exists.
Use soql_query:
Case["Id", "CaseNumber", "Status", "Priority", "OwnerId", "Owner.Name", "Subject", "Description", "IsClosed", "AccountId", "Account.Name"]Id = '{caseId}' (if ID format) or CaseNumber = '{caseId}' (if number format)1Step 2: Apply business rules:
IsClosed = true and new status is not "Closed", reject: "Cannot reopen a closed case. Please create a new case instead."references/status-transitions.md)| Current Status | Allowed Transitions |
|---|---|
| New | Working, Escalated, Closed |
| Working | Escalated, Closed |
| Escalated | Working, Closed |
| Closed | (none — cannot reopen) |
Use the Case Id from the Step 1 query result (not the raw user input, which may be a CaseNumber).
Use sobject_dml:
Caseupdate[
{
"Id": "{case.Id}",
"Status": "{newStatus}"
}
]
Add optional fields if provided: Priority, OwnerId (resolve user first via soql_query on User).
Create audit trail if a reason was provided:
Use sobject_dml:
CaseCommentinsert[
{
"ParentId": "{case.Id}",
"CommentBody": "Status changed from {previousStatus} to {newStatus}. Reason: {reason}",
"IsPublished": false
}
]
Present results with the updated case number, new status, and a link.
When a user wants to create a support case linked to a return order:
Step 1: Get the return order.
Use soql_query:
ReturnOrder["Id", "ReturnOrderNumber", "OrderId", "Status", "Description", "AccountId", "Account.Name", "CaseId"]Id = '{returnOrderId}' (if ID format) or ReturnOrderNumber = '{returnOrderId}' (if number format)1Step 2: Get return line items (use the ReturnOrder Id from Step 1, not raw user input).
Use soql_query:
ReturnOrderLineItem["Id", "Product2Id", "Product2.Name", "QuantityReturned", "Description"]ReturnOrderId = '{returnOrder.Id}'200Step 3: Business validation:
CaseId is already populated, reject: "A case already exists for this return order. Case ID: {CaseId}"Step 4: Get order details for context.
Use soql_query:
Order["Id", "OrderNumber", "AccountId", "Account.Name", "TotalAmount"]Id = '{orderId_from_return}'1Determine priority based on return line item descriptions:
Use sobject_dml:
Caseinsert[
{
"Subject": "Return Order Issue - {ReturnOrderNumber}",
"Description": "Case created for return order {ReturnOrderNumber}.\n\nOrder: {OrderNumber}\nReturn Status: {Status}\nReturn Description: {Description}\n\nReturning Items:\n1. {Product Name} (Qty: {QuantityReturned}) - {Description}",
"Status": "New",
"Priority": "<determined_priority>",
"Origin": "Web",
"Type": "Other",
"AccountId": "{accountId_from_order}"
}
]
Link the case back to the return order (use the ReturnOrder Id from Step 1):
Use sobject_dml:
ReturnOrderupdate[
{
"Id": "{returnOrder.Id}",
"CaseId": "{new_case_id}"
}
]
Note: The CaseId field on ReturnOrder is a standard field. If the update fails (field not available), log the warning but don't fail — the case was still created.
Slack webhook alerts and other external notifications are not handled by the Cirra AI MCP Server directly. If the user needs notification workflows:
sf-flow skill to create a notification flowFor all operations, follow these patterns:
Always include:
link_buildTable format for order items:
| # | Product | Quantity | Unit Price | Total |
|---|---------|----------|------------|-------|
Table format for return items:
| # | Product | Qty Returned | Description |
|---|---------|-------------|-------------|
ReturnOrder object not found: The org needs Service Cloud or Order Management enabled. ReturnOrder is a standard object but requires specific licenses.
Cannot create ReturnOrderLineItem — OrderItemId not valid: Verify the OrderItem belongs to the Order referenced by the ReturnOrder. Query OrderItems where OrderId matches.
ReturnOrder status won't change to "Submitted": Check if there are validation rules or process builders that prevent the transition. Some orgs require line items before status can change.
CaseId field not available on ReturnOrder: Standard field but may not be visible in all editions. The case is still created; just the link back won't be set. The user can create a custom lookup if needed.
Return quantity exceeds original order quantity: Query the OrderItem to get the original Quantity and validate before creating the return.