Diagnose a stuck shipment

Updated May 29, 20263 min read

A shipment hasn't moved in a while. An ops user — or a customer-support agent — wants to understand why, and what to do about it.

This is the most common multi-tool MCP pattern we see. The agent reads state from three angles (Carriyo-internal, activity, carrier-side system logs), narrates a diagnosis, and proposes (or takes) the next action.

When to use

  • Customer service: "Where's order 8842?"
  • Internal ops: "Why is SHP-12345 stuck on READY_FOR_PICKUP for two days?"
  • Bulk triage: "Find all shipments stuck >24 hours and tell me what's wrong." (Use get_shipment_list filtered by creation_date_* and status to find candidates, then loop the diagnosis pattern.)

Tool sequence

get_shipment                      (current state)
       │
       ▼
get_shipment_activity             (Carriyo-internal events: status changes, rules, notifications)
       │
       ▼
list_shipment_system_logs         (carrier-side log entries — find errors)
       │
       ▼
get_shipment_system_log           (full request/response for the most relevant entry)
       │
       ▼
   diagnosis
       │
       ├── (carrier never picked up)        →  reprocess_shipment
       ├── (carrier rejected — capacity / address / weight)  →  reassign_shipment
       ├── (label issue — bad PDF, expired URL)  →  refresh_label
       └── (delivery failed, can retry)     →  schedule_delivery

Example agent prompt

"SHP-12345 hasn't moved in two days. What's going on, and what should I do about it?"

Walkthrough

Step 1 — pull the shipment

get_shipment(tenantId="…", shipment_id="SHP-12345")

Returns the current status, status timestamp, carrier, promised dates, addresses. The agent now knows what state the shipment is in and for how long — but not why.

Step 2 — pull the activity timeline

get_shipment_activity(tenantId="…", shipment_id="SHP-12345")

Activity is everything Carriyo-internal that's happened on the shipment record: creation, status transitions, rule assignments, notifications sent, customer feedback. If the issue is on the Carriyo side — a stuck rule, a notification that didn't fire, a user action that broke flow — it shows up here.

Step 3 — pull carrier-side system logs

list_shipment_system_logs(
  tenantId="…",
  shipment_id="SHP-12345",
  responseCode="5xx"
)

System logs are the carrier-side view: every request Carriyo made to the carrier, the response code, and the response body. Filtering on responseCode="5xx" (or 4xx) narrows to errors. If the carrier rejected a booking, returned a malformed label, or hit a transient error, it lives here.

Step 4 — pull the full log body

get_shipment_system_log(
  tenantId="…",
  shipment_id="SHP-12345",
  identifier="…"      (the identifier of the most relevant entry from step 3)
)

Full request / response details. The agent reads the carrier's error message and uses it to decide what's wrong.

Step 5 — narrate the diagnosis

The agent now has three views of the same shipment. Common patterns:

SymptomLikely causeRecommended action
Confirmed, no carrier success logs, no tracking eventsCarrier didn't pick upreprocess_shipment (re-book with same carrier)
Carrier returned an error response in system logsCarrier rejected (capacity, address, weight)reassign_shipment (different carrier)
Multiple failed delivery attempts in activityCustomer unavailableschedule_delivery (new slot)
Label issue — generation failed, URL expiredBad label staterefresh_label, then physical reconciliation

The agent narrates the diagnosis in plain English ("Looks like Aramex rejected this one — their API returned a 500 on the booking call. Address might be the issue. I'd reassign to a different carrier.") and either prompts the human for confirmation or takes the action directly.

Step 6 — take the action

Based on the diagnosis:

reprocess_shipment(tenantId="…", shipment_id="SHP-12345")

or

reassign_shipment(tenantId="…", shipment_id="SHP-12345", carrier_id="dhl-uae-2")
Confirm before write tools

In a customer-facing agent, always confirm before invoking a write tool. The agent's diagnosis can be wrong, and a wrong reassignment costs real money (label fees, capacity reservations). See Limits & security → recommended guardrails.

Variations

  • Bulk triage — open with get_shipment_list filtered by creation_date_from/creation_date_to plus a status filter, then loop the diagnosis pattern over each result. Mind the rate limits (see Limits & security).
  • Customer-facing version — same pattern but the action step is always "create a support ticket" rather than reprocess_* / reassign_*. Restrict the agent's tool catalog accordingly (see Limits & security → Tool-catalog restriction).
  • Routing-decision variant — when the question is "why was this routed to carrier X?" rather than "why is it stuck?" — pair get_shipment with get_automation_ruleset and list_automation_rules to explain the routing decision.