> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stateset.com/llms.txt
> Use this file to discover all available pages before exploring further.

# From 'where is my order?' to a closed operation

> Follow one customer message across four engines — a ResponseCX agent picks it up, the Sync Server surfaces the late shipment, the Temporal Engine re-dispatches durably, NSR verifies the goodwill credit — until the operation, not just the conversation, is closed.

"Where is my order?" is the most common message in commerce support, and the easiest one to
answer badly: paste a tracking link, apologise, close the ticket. The shipment is still late.
StateSet's thesis is **close the operation, not just the conversation** — and this page proves it
by following a single message from one customer, Ada Lovelace, about one order, `ORD-10042`, for
one brand, `acme-outdoors`, across all four engines. The conversation is answered by an agent,
the answer is grounded in the real order book, the late shipment is re-dispatched by a durable
workflow, and the goodwill credit only executes behind a machine-checked decision.

```mermaid theme={null}
sequenceDiagram
    participant Ada as Ada (customer)
    participant RCX as ResponseCX agent
    participant Sync as Sync Server
    participant TE as Temporal Engine
    participant NSR as NSR

    Ada->>RCX: "Where is my order? ORD-10042"
    RCX->>Sync: GET /orders?shopify_order_id=…
    Sync-->>RCX: shipped — estimated delivery 3 days ago
    RCX->>TE: POST /v1/workflows/order-fulfillment/start (re-dispatch)
    TE-->>RCX: workflow_id · reserve → NSR gate → dispatch
    RCX->>NSR: POST /v1/decisions — may_credit(ORD-10042)?
    NSR-->>RCX: approved + verifiable proof bundle
    RCX->>Ada: "Replacement ships today, and a $15 credit is on your account"
    RCX->>NSR: POST /v1/decisions/outcome-by-ref (honored)
```

<Note>
  Four engines means four surfaces — each has its **own host, its own key and its own auth header**:
  [ResponseCX](/response-api-reference/overview) is `https://response.stateset.com/api/v1` with
  `Authorization: Bearer rcx_…`; the [Sync Server](/api-reference/sync/overview) is
  `https://api.sync.stateset.com` with `x-stateset-api-key: ss_sync_…`; the
  [Temporal Engine](/api-reference/temporal/overview) is `https://api.workstream.stateset.com` with
  `x-api-key: sk_live_…`; and [NSR](/api-reference/nsr/overview) is `https://api.nsr.stateset.com`
  with `X-API-Key: nsr_…`. A key from one engine means nothing to another.
</Note>

The examples assume all four are exported:

```bash theme={null}
export RESPONSECX_API_KEY="rcx_…"                                  # ResponseCX
export SYNC="https://api.sync.stateset.com/v1/tenants/acme-outdoors"
export SYNC_KEY="ss_sync_…"                                        # Sync Server
export STATESET_API_KEY="sk_live_…"                                # Temporal Engine (brand key)
export NSR_API_KEY="nsr_…"                                         # NSR
```

<Steps>
  <Step title="The message lands in a ResponseCX conversation">
    Ada writes on chat and the brand's ResponseCX agent picks the conversation up. Everything the
    rest of this page does hangs off this one record. Read it back — a single-conversation read
    includes the message transcript (`from_agent: false` marks the customer's side); the id comes
    from your inbox webhook, or from `GET /api/v1/conversations?status=open&channel=chat`.

    ```bash theme={null}
    curl https://response.stateset.com/api/v1/conversations/1d4f2a6b-9c3e-4b81-a7d5-0e8f6c2b9a31 \
      --header "Authorization: Bearer $RESPONSECX_API_KEY"
    ```

    ```json theme={null}
    { "object": "conversation", "id": "1d4f2a6b-9c3e-4b81-a7d5-0e8f6c2b9a31",
      "channel": "chat", "status": "open", "escalated": false,
      "agent_id": "7c2e9f41-3a8b-4d06-b1c5-9e0d2f7a8b64", "subject": "Where is my order?",
      "created_at": "2026-08-31T14:02:11Z",
      "messages": [
        { "object": "message", "id": "msg_01…", "from_agent": false, "author": "Ada Lovelace",
          "body": "Hi — where is my order? It's ORD-10042 and it was supposed to arrive Friday.",
          "created_at": "2026-08-31T14:02:11Z" } ] }
    ```

    **Why this matters:** the conversation is the thread of record. Its `status` is what we come
    back to at the end — an operation is not closed while this says `open`.
  </Step>

  <Step title="The agent's answer is grounded by a function, not a guess">
    A ResponseCX agent answers from what its **functions** return, and a function is just an HTTP
    call the agent may make. Give the agent a `lookup_order` tool that points straight at the Sync
    Server's tenant order book — the Sync key travels in the function's stored `headers`, so the
    agent never holds it. `POST /api/v1/agents/{id}/functions` needs the `agents:write` scope;
    send an `Idempotency-Key` so a retried create cannot register the tool twice.

    ```bash theme={null}
    curl --request POST https://response.stateset.com/api/v1/agents/7c2e9f41-3a8b-4d06-b1c5-9e0d2f7a8b64/functions \
      --header "Authorization: Bearer $RESPONSECX_API_KEY" \
      --header "Content-Type: application/json" \
      --header "Idempotency-Key: acme-outdoors-lookup-order-v1" \
      --data '{
        "function_name": "lookup_order",
        "endpoint": "https://api.sync.stateset.com/v1/tenants/acme-outdoors/orders",
        "method": "GET",
        "description": "Look up an acme-outdoors order (status, line items, tracking) by Shopify order id or order number.",
        "parameters": [
          { "name": "shopify_order_id", "type": "string", "description": "Shopify order id", "required": false },
          { "name": "order_number", "type": "string", "description": "Order number, e.g. ORD-10042", "required": false }
        ],
        "headers": { "x-stateset-api-key": "ss_sync_…" },
        "activated": true
      }'
    ```

    ```json theme={null}
    { "object": "function", "id": "f3b8d1c6-5e2a-4f90-8c7b-1a9d0e4f6b23", "name": "lookup_order",
      "endpoint": "https://api.sync.stateset.com/v1/tenants/acme-outdoors/orders", "method": "GET",
      "activated": true, "agent_id": "7c2e9f41-3a8b-4d06-b1c5-9e0d2f7a8b64" }
    ```

    **Why this matters:** "close the operation" starts with refusing to answer from memory. The
    agent's reply about ORD-10042 will be whatever the order book actually says, fetched at the
    moment Ada asks.
  </Step>

  <Step title="The Sync Server tells the truth: shipped, and late">
    This is the call the agent's function makes. The Sync Server keeps the tenant's order book in
    agreement with Shopify and the 3PL, so one lookup returns the order **and** its tracking:

    ```bash theme={null}
    curl "$SYNC/orders?shopify_order_id=6120058241234" --header "x-stateset-api-key: $SYNC_KEY"
    ```

    ```json theme={null}
    { "items": [ {
        "id": "0f9b7c3a-1d24-4e6b-8f1a-5b3d2c9e7a40",
        "shopifyOrderId": "6120058241234", "orderNumber": "ORD-10042", "status": "shipped",
        "lineItems": [ { "title": "Trail Pack 32L", "sku": "TP-32-GRN", "quantity": 1, "price": 129.00 } ],
        "fulfillmentStatus": "shipped",
        "tracking": { "trackingNumber": "1Z999AA10123456784", "carrier": "UPS",
                      "trackingUrl": "https://…", "estimatedDelivery": "2026-08-28" } } ],
      "pagination": { "page": 1, "pageSize": 20, "hasNext": false, "hasPrevious": false } }
    ```

    Today is the 31st and `estimatedDelivery` was the 28th: shipped, in-transit, three days late.
    For a live carrier scan beyond the order book, `POST $SYNC/dcl/tracking` with
    `{ "orderNumbers": ["ORD-10042"] }` fetches tracking straight from the 3PL.

    **Why this matters:** the same lookup that lets the agent answer honestly ("it shipped UPS on
    time, and it is now three days past its estimate") is what turns a chat reply into an
    operational fact the next two engines can act on.
  </Step>

  <Step title="The Temporal Engine re-dispatches the shipment durably">
    A late in-transit package is a remediation, not a paragraph. Start the engine's
    `order-fulfillment` workflow — **reserve inventory → NSR gate → (human review) → dispatch** —
    for a replacement shipment under its own order id, `ORD-10042-R1`. The workflow id is
    deterministic per `(brand, order_id)`, so a duplicate trigger returns the existing run instead
    of shipping twice.

    ```bash theme={null}
    curl --request POST https://api.workstream.stateset.com/v1/workflows/order-fulfillment/start \
      --header "x-api-key: $STATESET_API_KEY" \
      --header "content-type: application/json" \
      --data '{
        "brand_id": "6f1c2a3e-9b7d-4e21-a5c8-0d2f4b6e8a10",
        "order_id": "ORD-10042-R1",
        "order_total_cents": 12900,
        "currency": "USD",
        "line_items": [ { "sku": "TP-32-GRN", "quantity": 1 } ],
        "reservation_params": { "order_id": "ORD-10042-R1", "warehouse": "reno-01" },
        "fulfillment_tool": "create_order",
        "fulfillment_params": { "order_id": "ORD-10042-R1", "ship_method": "expedited" }
      }'
    ```

    ```json theme={null}
    { "workflow_id": "order-fulfillment-6f1c2a3e9b7d4e21a5c80d2f4b6e8a10-ORD-10042-R1",
      "run_id": "b8e2f6a1-4c9d-4e37-a2b8-5f0e1d3c7a92" }
    ```

    At $129 the total sits under the $500 autonomy cap, so nothing parks at the review gate: the
    workflow reserves the replacement, clears its built-in NSR gate, and dispatches.
    `GET /v1/workflows/order-fulfillment/{workflow_id}/status` moves from `"running:dispatch"` to
    `"fulfilled:dispatch"`; a connector blip mid-run is retried up to 8 times with the workflow's
    state untouched, and any terminal status that never dispatched releases the reservation.

    **Why this matters:** the agent's promise ("a replacement ships today") is now a durable
    execution, not an intention. If the process crashes, the promise survives the crash.
  </Step>

  <Step title="NSR verifies the goodwill credit before it executes">
    Three days late deserves a credit — but a credit is money, so it goes through a verified
    decision, not an agent's vibes. The brand's goodwill policy lives in NSR the same way the
    refund policy does in the [verified-decision guide](/guides/nsr-first-verified-decision) — a
    `may_credit` permit rule requiring `delivery_late`, plus a review rule that stops credits over
    \$50 — installed once via `POST /api/v1/rules/batch`. The live facts travel in the request, the
    stored policy is hydrated in, and declaring an `authorization_goal` is what makes an approval
    carry a proof bundle you can check yourself.

    ```bash theme={null}
    curl --request POST https://api.nsr.stateset.com/v1/decisions \
      --header "X-API-Key: $NSR_API_KEY" \
      --header "Content-Type: application/json" \
      --header "Idempotency-Key: goodwill-ORD-10042-attempt-1" \
      --data '{
        "query": "May a goodwill credit be issued for ORD-10042?",
        "action": "issue_goodwill_credit",
        "mode": "safe",
        "facts": [
          { "predicate": { "name": "delivery_late", "args": ["ORD-10042"] },      "confidence": 1.0, "source": "sync" },
          { "predicate": { "name": "credit_amount", "args": ["ORD-10042", 15] },  "confidence": 1.0, "source": "agent" }
        ],
        "authorization_goal": { "name": "may_credit", "args": ["ORD-10042"] },
        "external_ref": "ORD-10042",
        "hydrate_org_context": true
      }'
    ```

    ```json theme={null}
    { "decision_id": "dec_01jc4…", "decision": "approved", "confidence": 0.95,
      "evaluated_goal": "may_credit(ORD-10042)",
      "plain_explanation": "Approved to issue a goodwill credit for ORD-10042: the delivery for ORD-10042 is late and the credit is within policy.",
      "proof": { "cited_rules": ["goodwill_ok"], "proof_status": "grounded", "request_policy_hash": "sha256:9c2e…" },
      "verifiable_bundle": { "facts": ["…"], "rules": ["…"], "proof": { "…": "…" }, "org_id": "org_acme" } }
    ```

    Only after `POST /v1/proofs/verify` returns `{ "verified": true }` on that bundle does the
    agent's credit tool fire — a function registered exactly like `lookup_order` in step 2,
    pointing at your commerce backend. Every other path — `denied`, `refused`, a timeout, an
    unreachable engine — ends **without** a side effect. Had Ada been owed \$60 instead, the
    review rule would answer `refused` with `requires_human_review: true`, and the credit would
    wait for a person.

    **Why this matters:** the goodwill credit is the one step here that moves money on an agent's
    initiative. Gating it on a machine-checkable proof is the difference between an autonomous
    operation and an expensive one.
  </Step>

  <Step title="Record the outcome against the decision">
    Weeks later, the system that learns the truth — billing, the ledger, a chargeback feed —
    closes the loop on the decision by the order id it already holds:

    ```bash theme={null}
    curl --request POST https://api.nsr.stateset.com/v1/decisions/outcome-by-ref \
      --header "X-API-Key: $NSR_API_KEY" \
      --header "Content-Type: application/json" \
      --data '{ "external_ref": "ORD-10042", "outcome": "honored" }'
    ```

    **Why this matters:** reported outcomes feed `GET /v1/decisions/calibration`, which tells you
    whether a `0.95` confidence holds up 95% of the time — and therefore how much of this journey
    can defensibly run with no human in it at all.
  </Step>

  <Step title="Confirm the conversation — and the operation — is closed">
    The agent replies to Ada in-channel: the package is three days late, a replacement is
    dispatched expedited, a \$15 credit is on her account. Resolution itself happens where the
    conversation lives — the agent (or a teammate in the dashboard) closes it on the platform;
    the public API is the audit surface, so read the record back:

    ```bash theme={null}
    curl https://response.stateset.com/api/v1/conversations/1d4f2a6b-9c3e-4b81-a7d5-0e8f6c2b9a31 \
      --header "Authorization: Bearer $RESPONSECX_API_KEY"
    ```

    ```json theme={null}
    { "object": "conversation", "id": "1d4f2a6b-9c3e-4b81-a7d5-0e8f6c2b9a31",
      "channel": "chat", "status": "closed", "escalated": false, "rating": "positive",
      "agent_id": "7c2e9f41-3a8b-4d06-b1c5-9e0d2f7a8b64", "subject": "Where is my order?" }
    ```

    **Why this matters:** `status: "closed"` is honest here precisely because of everything above
    it — a re-dispatch is running durably, a credit executed behind a verified proof, and the
    decision has an outcome trail. The conversation closed because the operation did.
  </Step>
</Steps>

## What you built

| Piece                    | Engine          | Endpoint                                                | What it does                                                                                                        |
| ------------------------ | --------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| Conversation of record   | ResponseCX      | `GET /api/v1/conversations/{id}`                        | The thread whose `status` defines "closed", with the capped message transcript                                      |
| Grounded lookup tool     | ResponseCX      | `POST /api/v1/agents/{id}/functions`                    | Lets the agent answer from the order book instead of from memory                                                    |
| Order + tracking truth   | Sync Server     | `GET /v1/tenants/{tenant_id}/orders?shopify_order_id=…` | One tenant-scoped read: status, line items, carrier, and the estimate that proves lateness                          |
| Durable re-dispatch      | Temporal Engine | `POST /v1/workflows/order-fulfillment/start`            | Reserve → NSR gate → dispatch for the replacement, idempotent per `(brand, order_id)`, retried through failures     |
| Verified goodwill credit | NSR             | `POST /v1/decisions`                                    | `approved` / `denied` / `refused` with cited rules and a proof bundle; the credit fires only on a verified approval |
| Outcome loop             | NSR             | `POST /v1/decisions/outcome-by-ref`                     | Turns the credit's real-world result into calibration for future autonomy                                           |

## Next steps

Each leg of this journey has a full per-engine guide that goes deeper than one step could:

<CardGroup cols={2}>
  <Card title="ResponseCX quickstart" icon="comments" href="/quickstart">
    Create the agent itself, launch it, and read back its rules, functions and knowledge.
  </Card>

  <Card title="Sync a Shopify store with NetSuite" icon="rotate" href="/guides/sync-shopify-to-netsuite">
    Where the order book comes from — syncs, jobs, failure recovery and webhooks for one tenant.
  </Card>

  <Card title="Run a durable order workflow" icon="diagram-project" href="/guides/temporal-first-durable-workflow">
    The same order-fulfillment workflow end to end — review gates, event streams, cancellation and replay.
  </Card>

  <Card title="Gate a refund with a verified decision" icon="gavel" href="/guides/nsr-first-verified-decision">
    Store the policy, read all three verdicts, verify the proof without trusting the engine, and fail closed.
  </Card>
</CardGroup>
