FS-DOC-05Technical
Integration and API Spec
Delivery Exception Handling
[YourCompany.com] · Fulfilment Department · Prepared by FullSpec · [Today's Date]
This document is the authoritative integration reference for the Delivery Exception Handling automation. It covers every tool connected in the workflow, the exact scopes and credentials required, field mappings between systems, orchestration architecture, and the required error-handling behaviour at each integration point. The FullSpec team uses this specification to build, configure, and validate every connection. Your team's responsibility is to supply the API credentials and confirm the configuration values marked as business-defined before build begins.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
ShipStation
Carrier exception webhook source; polling fallback trigger
API key + API secret (Basic auth over HTTPS)
Starter ($9.99/month) or above; webhook access available on all paid plans
Agent 1: Exception Triage Agent
Shopify
Order lookup; reship (draft order creation); refund processing
OAuth 2.0 (private app API key + secret, or custom app with scopes)
Basic Shopify or above; Admin API access included
Agent 1: Exception Triage Agent; Agent 2: Resolution Agent
Gorgias
Auto-create and tag support tickets; record resolution outcome
API key + base URL (Basic auth, email:api_key encoded)
Basic plan or above; REST API access included
Agent 2: Resolution Agent
Gmail
Send templated customer exception notification emails
OAuth 2.0 (Google service account or user OAuth token)
Free Google Workspace account; Gmail API enabled in Google Cloud Console
Agent 2: Resolution Agent
Slack
Manager approval prompt for high-value orders; team resolution summary alert
OAuth 2.0 (Slack app bot token via App installation)
Free or paid Slack workspace; incoming webhooks and Bot token scopes
Agent 2: Resolution Agent
Google Sheets
Exception log and resolution audit trail; append row on each resolved case
OAuth 2.0 (Google service account with Sheets API enabled)
Free Google account; Google Sheets API v4 enabled in Google Cloud Console
Agent 2: Resolution Agent
Orchestration layer
Workflow automation platform hosting all agent workflows, credential store, retry logic, and routing
Internal platform credential store; no external auth
Platform tier sufficient to support webhook receivers, multiple HTTP nodes, and scheduled polling
All agents
Before you connect anything: test every integration against a sandbox or staging environment using non-production credentials before any production API key or OAuth token is entered. ShipStation supports a test webhook endpoint; Shopify provides a development store; Gorgias has a sandbox subdomain on request; Gmail and Google Sheets should be tested against a dedicated [YourCompany.com] test account. Only promote to production credentials after all per-tool tests pass.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
02Per-tool integration detail
ShipStation
ShipStation is the automation entry point. It delivers a webhook payload to the orchestration layer the moment a shipment status changes to a failed, held, or undeliverable state. A 15-minute polling fallback is required for carriers that do not push status changes reliably.
Auth method
HTTP Basic Auth: Base64-encoded API_KEY:API_SECRET sent in the Authorization header on all outbound API calls. Webhook payloads are sent by ShipStation with no built-in HMAC signature; validate the source IP range (documented in ShipStation support) or use a secret token appended to the webhook URL as a query parameter stored in the credential store.
Required scopes
ShipStation uses API key-level access rather than OAuth scopes. The API key must be generated by an account with: read access to Shipments, read access to Orders, and access to Webhook management (create/list/delete webhooks). No write scopes are needed on ShipStation itself.
Webhook setup
Register a webhook in ShipStation under Settings > Integrations > Webhooks. Event type: SHIP_NOTIFY and On_Ship_Status_Change. Endpoint URL: the orchestration layer's inbound webhook URL for the Exception Triage Agent workflow. Verify the endpoint returns HTTP 200 within 10 seconds or ShipStation will retry up to 3 times with no backoff.
Polling fallback
Schedule a GET /shipments call every 15 minutes filtered by shipStatus=Exception and modifyDateStart equal to the timestamp of the last successful poll. Store the last-polled timestamp in the workflow's state variable. De-duplicate against the exceptions already processed in the current run using the shipmentId field.
Required configuration
SHIPSTATION_API_KEY and SHIPSTATION_API_SECRET stored in credential store (never hardcoded). SHIPSTATION_WEBHOOK_SECRET_TOKEN stored in credential store and appended to the webhook receiver URL. The carrier exception code mapping table (e.g. USPS: A1, FedEx: 03.7, UPS: X, DHL: CA) must be configured in the workflow before go-live.
Rate limits
ShipStation REST API: 40 requests per minute on Starter; 80 requests per minute on higher tiers. At 90 exceptions/month (~3/day) the polling fallback generates at most 96 GET requests/day, well within the 40/minute burst limit. No throttling middleware is required at current volume.
Constraints
Webhook delivery is not guaranteed real-time for all carrier integrations. The polling fallback is mandatory. ShipStation does not support filtered webhooks by exception type; the orchestration layer must filter on statusCode or carrierStatusCode after receipt.
// Inbound webhook payload (key fields)
shipmentId : string // unique ShipStation shipment identifier
orderNumber : string // maps to Shopify order name (#1234)
carrierCode : string // e.g. 'usps', 'fedex', 'ups', 'dhl'
carrierStatusCode : string // raw exception code from carrier
carrierStatusDescription : string // human-readable exception message
trackingNumber : string
shipDate : string // ISO 8601
// Output to Exception Triage Agent
exceptionPayload : object // above fields plus parsed carrierCode and statusCode
Shopify
Shopify is called twice in the automation: once by the Exception Triage Agent to fetch the full order record, and once by the Resolution Agent to apply the reship (draft order) or refund. Both calls use the Admin REST API.
Auth method
Custom app API key and Admin API access token (X-Shopify-Access-Token header). Create a custom app in the Shopify admin under Apps > Develop apps. The access token is shown once on creation and must be stored immediately in the credential store.
Required scopes
read_orders, write_orders, read_customers, write_draft_orders, read_draft_orders, write_draft_orders, read_fulfillments, write_fulfillments. For refunds: write_orders covers order-level refunds via the Refund API.
Webhook / trigger setup
Shopify is called reactively (HTTP GET and POST), not as a webhook source in this automation. No Shopify webhooks are registered. All calls are initiated by the orchestration layer after the ShipStation event fires.
Required configuration
SHOPIFY_STORE_DOMAIN (e.g. yourcompany.myshopify.com) and SHOPIFY_ADMIN_API_TOKEN stored in credential store. SHOPIFY_ORDER_VALUE_THRESHOLD (the approval threshold dollar amount) stored as a workflow environment variable, not hardcoded, so it can be updated without a code change. API version pinned to a named stable release (e.g. 2024-04).
Rate limits
Shopify REST Admin API: 40 requests per app per store per minute (leaky bucket, refills at 2/second). At current volume (~3 exceptions/day) the automation makes at most 6 API calls/day (1 GET + 1 POST per exception). No throttling is required. Add a 500ms delay between consecutive calls as a precaution if batch processing catches up after a polling gap.
Constraints
Draft orders used for reships must be completed (POST /admin/api/2024-04/draft_orders/{id}/complete.json) to convert them to live orders; do not leave draft orders open. Refund calculations must call GET /admin/api/2024-04/orders/{id}/refunds/calculate.json before POST to the refunds endpoint to avoid rounding errors.
// GET /admin/api/2024-04/orders.json?name={orderNumber}&status=any
order.id : integer // Shopify internal order ID
order.name : string // #1234 format
order.email : string // customer email for Gmail step
order.total_price : string // string decimal, compare to threshold
order.shipping_address: object // address fields for triage
order.line_items[] : array // product titles and quantities
order.customer.first_name : string
// POST /admin/api/2024-04/draft_orders.json (reship)
draft_order.line_items: array // copied from original order.line_items
draft_order.shipping_address: object
draft_order.note : string // 'Reship: exception ref {exceptionId}'
// POST /admin/api/2024-04/orders/{id}/refunds.json
refund.shipping.full_refund: boolean
refund.note : string // 'Refund: exception ref {exceptionId}'Gorgias
Gorgias receives an API call from the Resolution Agent to create a support ticket immediately after exception classification. The ticket is tagged with the exception type and assigned to the fulfilment queue. The ticket ID is stored in the workflow state for later update.
Auth method
HTTP Basic Auth: Base64-encoded email_address:api_key. The API key is generated in Gorgias under Settings > REST API. Store GORGIAS_BASE_URL (e.g. https://yourcompany.gorgias.com), GORGIAS_EMAIL, and GORGIAS_API_KEY in the credential store.
Required scopes
Gorgias API keys are not scope-restricted by default; the key inherits the permissions of the generating user. Use a dedicated service account user with the Agent role. Required API endpoints: POST /api/tickets (create), PUT /api/tickets/{id} (update with resolution outcome), POST /api/tickets/{id}/messages (add internal note).
Webhook / trigger setup
Gorgias is not a trigger source in this automation. All calls are outbound from the orchestration layer. No Gorgias webhooks are registered.
Required configuration
GORGIAS_FULFILMENT_QUEUE_ID: the integer ID of the fulfilment queue (retrieve via GET /api/views and store in credential store, not hardcoded). GORGIAS_TAG_MAP: a key-value map of exception_type to Gorgias tag string, e.g. {address_issue: 'exc-address', failed_attempt: 'exc-attempt', customs_hold: 'exc-customs', lost_parcel: 'exc-lost'}, stored as a workflow variable.
Rate limits
Gorgias REST API: 40 requests per minute on Basic; 80/minute on Pro. At 90 exceptions/month the automation makes at most 180 ticket API calls/month (create + update). No throttling required at current volume.
Constraints
Ticket channel must be set to 'email' when creating via API to ensure the ticket appears in the agent view correctly. Assigning to a view (queue) requires the view ID, not the name string; this ID must be pre-fetched and stored. Do not create duplicate tickets: check the workflow state for an existing ticketId before calling POST /api/tickets.
// POST /api/tickets (create ticket)
ticket.channel : 'email'
ticket.subject : string // 'Exception: {exceptionType} - Order {orderName}'
ticket.assignee_team : {id: GORGIAS_FULFILMENT_QUEUE_ID}
ticket.tags[] : string[] // from GORGIAS_TAG_MAP lookup
ticket.messages[0].body_html : string // exception detail HTML
ticket.messages[0].from_agent: true
// Response
ticket.id : integer // store as workflowState.gorgiasTicketId
// PUT /api/tickets/{id} (update with resolution)
ticket.status : 'closed'
ticket.tags : append 'resolved-reship' or 'resolved-refund'Gmail
Gmail sends the customer-facing exception notification email using a template matched to the classified exception type. Four templates are required, one per exception category. The send is triggered by the Resolution Agent immediately after the Gorgias ticket is created.
Auth method
OAuth 2.0. Use a Google Cloud project with the Gmail API enabled. For server-side sends without user interaction, use a Google service account with domain-wide delegation, or a dedicated sending Gmail account with a long-lived OAuth refresh token stored in the credential store. Store GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REFRESH_TOKEN, and GMAIL_SENDER_ADDRESS in the credential store.
Required scopes
https://www.googleapis.com/auth/gmail.send (send-only; do not request broader mail scopes unless read access is required for another step).
Webhook / trigger setup
Gmail is an outbound action only in this automation. No Gmail webhook or Pub/Sub subscription is configured. The orchestration layer calls the Gmail API (POST /gmail/v1/users/me/messages/send) with a MIME-encoded message.
Required configuration
Four email templates stored as workflow variables (not hardcoded in node logic): TEMPLATE_ADDRESS_ISSUE, TEMPLATE_FAILED_ATTEMPT, TEMPLATE_CUSTOMS_HOLD, TEMPLATE_LOST_PARCEL. Each template uses placeholders: {{customer_first_name}}, {{order_name}}, {{carrier_name}}, {{tracking_number}}, {{resolution_steps}}. Templates must be approved by [YourCompany.com] before go-live.
Rate limits
Gmail API: 250 quota units per user per second; send message costs 100 units. Effective limit is ~2.5 sends/second. At 90 exceptions/month (~3/day) there is no risk of hitting rate limits. Gmail also enforces a 500 recipients/day limit per user on free accounts; use a Google Workspace account to raise this to 2,000/day.
Constraints
The MIME message must be base64url-encoded before sending. The From address must match the authenticated sender; mismatches cause 403 errors. Always set a Reply-To header pointing to the Gorgias ticket email address so customer replies route into the support queue automatically.
// Input from Resolution Agent
customer_email : string // order.email from Shopify
customer_first_name : string // order.customer.first_name
order_name : string // e.g. #1042
exception_type : string // one of: address_issue, failed_attempt, customs_hold, lost_parcel
carrier_name : string // human-readable carrier name
tracking_number : string
gorgias_reply_email : string // Gorgias ticket reply-to address
// Gmail API call
POST /gmail/v1/users/me/messages/send
body: { raw: base64url(MIMEmessage) }
// MIMEmessage headers
To: {customer_email}
From: {GMAIL_SENDER_ADDRESS}
Reply-To: {gorgias_reply_email}
Subject: 'Update on your order {order_name}'Slack
Slack is used for two distinct purposes: a manager approval prompt (interactive message with Approve/Reject buttons) when order value exceeds the configured threshold, and a non-interactive resolution summary posted to the fulfilment channel after each case is closed.
Auth method
OAuth 2.0 Slack app with a bot token (xoxb-). Create a Slack app in the Slack API dashboard, install it to the workspace, and store SLACK_BOT_TOKEN, SLACK_APPROVAL_CHANNEL_ID (the manager's private channel or DM), and SLACK_FULFILMENT_CHANNEL_ID (team summary channel) in the credential store.
Required scopes
chat:write (post messages), chat:write.public (post to public channels without joining), incoming-webhook (optional fallback), commands (if slash command approval is used). For interactive approval buttons: the app must have an Interactivity Request URL configured pointing to the orchestration layer's inbound webhook endpoint.
Webhook / trigger setup
Approval flow: the orchestration layer POSTs a Block Kit message with action buttons (Approve / Reject) to SLACK_APPROVAL_CHANNEL_ID. The Slack app's Interactivity and Shortcuts Request URL must point to the orchestration layer's Slack interaction receiver endpoint. The orchestration layer validates the request using the SLACK_SIGNING_SECRET (HMAC-SHA256 of the raw request body using the signing secret). Resolution summary: a simple chat.postMessage call to SLACK_FULFILMENT_CHANNEL_ID, no interaction required.
Required configuration
SLACK_BOT_TOKEN, SLACK_SIGNING_SECRET, SLACK_APPROVAL_CHANNEL_ID, SLACK_FULFILMENT_CHANNEL_ID stored in credential store. APPROVAL_TIMEOUT_SECONDS (default: 86400, i.e. 24 hours) stored as a workflow variable. If no approval response is received within this window the workflow posts a reminder and pauses for a further 4 hours before escalating.
Rate limits
Slack Web API: tier 3 methods (chat.postMessage) allow 1 request/second with burst tolerance. At current volume this automation posts at most 6 Slack messages/day. No throttling required.
Constraints
Interactive approval messages must include a unique action_id and a callback_id that the orchestration layer uses to resume the correct paused workflow instance. Block Kit messages expire after a configurable period; update the original message (chat.update) with the decision outcome after the manager acts, to prevent re-clicking.
// Approval prompt message (Block Kit)
channel : SLACK_APPROVAL_CHANNEL_ID
blocks[].type : 'section' | 'actions'
blocks[].text.text : 'Exception: {exceptionType} on order {orderName} ({orderValue}). Recommended: {recommendedResolution}.'
blocks[].elements[0].action_id : 'approve_resolution'
blocks[].elements[1].action_id : 'reject_resolution'
metadata.callback_id : workflowInstanceId // used to resume correct workflow
// Inbound interaction payload (from Slack to orchestration layer)
payload.actions[0].action_id : string // 'approve_resolution' or 'reject_resolution'
payload.user.id : string // Slack user ID of approver (log for audit)
payload.message.ts : string // message timestamp for chat.update
// Resolution summary message
channel : SLACK_FULFILMENT_CHANNEL_ID
text : 'Resolved: Order {orderName} | {exceptionType} | {resolutionType} | {timestamp}'Google Sheets
Google Sheets receives a row append call from the Resolution Agent after every exception is resolved. It is the persistent audit log for all exceptions. The sheet structure must be created before go-live and must not be modified after the automation is live without updating the column index mapping in the workflow.
Auth method
OAuth 2.0 using a Google service account. Share the target Google Sheet with the service account email address (editor access). Store GOOGLE_SERVICE_ACCOUNT_KEY_JSON (the full service account JSON key, base64-encoded) and SHEETS_SPREADSHEET_ID in the credential store.
Required scopes
https://www.googleapis.com/auth/spreadsheets (read/write to spreadsheets). If the service account also sends Gmail, add https://www.googleapis.com/auth/gmail.send to the same service account. Do not request drive.file or drive unless broader file access is needed.
Webhook / trigger setup
Google Sheets is an outbound action only. The orchestration layer calls the Sheets API (POST spreadsheets/{id}/values/{range}:append) to append a single row per resolved exception. No Sheets-side triggers or Apps Script are used.
Required configuration
SHEETS_SPREADSHEET_ID stored in credential store. SHEETS_LOG_SHEET_NAME (default: 'Exception Log') stored as workflow variable. The sheet must have the following headers in row 1, columns A through J: Timestamp, Order Name, Order Value, Exception Type, Carrier, Tracking Number, Resolution Type, Approved By, Gorgias Ticket ID, Notes. Column positions must not change after go-live.
Rate limits
Google Sheets API v4: 300 read requests per minute per project; 300 write requests per minute per project. At 90 exceptions/month (approximately 3 appends/day) there is no risk of hitting limits. No throttling required.
Constraints
Use valueInputOption=USER_ENTERED so date and currency values render correctly in Sheets. Use insertDataOption=INSERT_ROWS to prevent overwriting existing data. The spreadsheet must not use merged cells in the log range. If the spreadsheet is moved to a different Google Drive folder its ID does not change; if it is deleted and recreated the ID changes and the credential store must be updated.
// POST spreadsheets/{SHEETS_SPREADSHEET_ID}/values/Exception Log!A:J:append
valueInputOption : 'USER_ENTERED'
insertDataOption : 'INSERT_ROWS'
body.values[0][0] : timestamp // ISO 8601, e.g. '2024-06-03T14:22:00Z'
body.values[0][1] : order.name // e.g. '#1042'
body.values[0][2] : order.total_price // e.g. '$124.00'
body.values[0][3] : exception_type // e.g. 'address_issue'
body.values[0][4] : carrier_name // e.g. 'USPS'
body.values[0][5] : tracking_number
body.values[0][6] : resolution_type // 'reship' or 'refund'
body.values[0][7] : approved_by // Slack user ID or 'auto'
body.values[0][8] : gorgias_ticket_id // integer
body.values[0][9] : notes // free text or blankIntegration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The tables below document every field handoff between tools at each agent boundary. Use exact field names as shown; these must match the variable names used in the orchestration layer's node configurations.
Handoff 1: ShipStation webhook to Exception Triage Agent (Shopify lookup)
Source tool
Source field
Destination tool
Destination field
ShipStation
orderNumber
Shopify
orders?name={orderNumber}
ShipStation
carrierCode
Triage Agent state
workflowState.carrierCode
ShipStation
carrierStatusCode
Triage Agent state
workflowState.carrierStatusCode
ShipStation
carrierStatusDescription
Triage Agent state
workflowState.carrierStatusDescription
ShipStation
trackingNumber
Triage Agent state
workflowState.trackingNumber
ShipStation
shipmentId
Triage Agent state
workflowState.shipmentId
ShipStation
shipDate
Triage Agent state
workflowState.shipDate
Handoff 2: Shopify order record to Exception Triage Agent classification
Source tool
Source field
Destination tool
Destination field
Shopify
order.id
Triage Agent state
workflowState.shopifyOrderId
Shopify
order.name
Triage Agent state
workflowState.orderName
Shopify
order.email
Triage Agent state
workflowState.customerEmail
Shopify
order.customer.first_name
Triage Agent state
workflowState.customerFirstName
Shopify
order.total_price
Triage Agent state
workflowState.orderValue
Shopify
order.shipping_address
Triage Agent state
workflowState.shippingAddress
Shopify
order.line_items
Triage Agent state
workflowState.lineItems
Handoff 3: Exception Triage Agent classification to Resolution Agent (Gorgias and Gmail)
Source tool
Source field
Destination tool
Destination field
Triage Agent state
workflowState.exceptionType
Gorgias
ticket.tags[] (via GORGIAS_TAG_MAP)
Triage Agent state
workflowState.exceptionType
Gorgias
ticket.subject (interpolated)
Triage Agent state
workflowState.orderName
Gorgias
ticket.subject (interpolated)
Triage Agent state
workflowState.carrierStatusDescription
Gorgias
ticket.messages[0].body_html
Triage Agent state
workflowState.exceptionType
Gmail
template selector (TEMPLATE_{EXCEPTION_TYPE})
Triage Agent state
workflowState.customerEmail
Gmail
MIMEmessage.To
Triage Agent state
workflowState.customerFirstName
Gmail
template placeholder {{customer_first_name}}
Triage Agent state
workflowState.orderName
Gmail
template placeholder {{order_name}}
Triage Agent state
workflowState.trackingNumber
Gmail
template placeholder {{tracking_number}}
Triage Agent state
workflowState.carrierCode
Gmail
template placeholder {{carrier_name}}
Handoff 4: Resolution Agent to Shopify (reship or refund) and Slack (approval prompt)
Source tool
Source field
Destination tool
Destination field
Triage Agent state
workflowState.orderValue
Approval gate
compare to SHOPIFY_ORDER_VALUE_THRESHOLD
Triage Agent state
workflowState.shopifyOrderId
Shopify
draft_orders.line_items (copied from original)
Triage Agent state
workflowState.shippingAddress
Shopify
draft_order.shipping_address
Triage Agent state
workflowState.shipmentId
Shopify
draft_order.note (exception reference)
Triage Agent state
workflowState.orderName
Slack
approval Block Kit message text
Triage Agent state
workflowState.orderValue
Slack
approval Block Kit message text
Triage Agent state
workflowState.exceptionType
Slack
approval Block Kit message text
Triage Agent state
workflowState.recommendedResolution
Slack
approval Block Kit message text
Slack
payload.actions[0].action_id
Resolution Agent state
workflowState.approvalDecision
Slack
payload.user.id
Resolution Agent state
workflowState.approvedBySlackUserId
Handoff 5: Resolution Agent to Google Sheets audit log and Slack summary
Source tool
Source field
Destination tool
Destination field
Resolution Agent state
workflowState.resolvedAt
Google Sheets
body.values[0][0] (Timestamp)
Resolution Agent state
workflowState.orderName
Google Sheets
body.values[0][1] (Order Name)
Resolution Agent state
workflowState.orderValue
Google Sheets
body.values[0][2] (Order Value)
Resolution Agent state
workflowState.exceptionType
Google Sheets
body.values[0][3] (Exception Type)
Resolution Agent state
workflowState.carrierCode
Google Sheets
body.values[0][4] (Carrier)
Resolution Agent state
workflowState.trackingNumber
Google Sheets
body.values[0][5] (Tracking Number)
Resolution Agent state
workflowState.resolutionType
Google Sheets
body.values[0][6] (Resolution Type)
Resolution Agent state
workflowState.approvedBySlackUserId
Google Sheets
body.values[0][7] (Approved By)
Resolution Agent state
workflowState.gorgiasTicketId
Google Sheets
body.values[0][8] (Gorgias Ticket ID)
Gorgias
ticket.id
Resolution Agent state
workflowState.gorgiasTicketId
Resolution Agent state
workflowState.resolutionType
Slack
fulfilment channel summary message text
Resolution Agent state
workflowState.orderName
Slack
fulfilment channel summary message text
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Two separate workflows, one per agent: 'Exception Triage Agent Workflow' and 'Resolution Agent Workflow'. The Triage workflow triggers on the ShipStation inbound webhook and passes its output state to the Resolution workflow via a shared in-memory context object or a persisted workflow state record, depending on platform capability. Both workflows share a single credential store; no credentials are duplicated across workflows.
Exception Triage Agent trigger
Webhook trigger: receives HTTP POST from ShipStation on shipment status change. The receiver endpoint URL is pre-registered in ShipStation webhook settings. Signature validation: HMAC-SHA256 of raw request body using SHIPSTATION_WEBHOOK_SECRET_TOKEN; reject any request where the computed signature does not match. Polling fallback: scheduled trigger every 15 minutes calling ShipStation GET /shipments with status filter; runs in parallel with webhook path and de-duplicates by shipmentId.
Resolution Agent trigger
Internal trigger: fired programmatically by the Exception Triage Agent workflow on successful completion of classification. Passes the full workflowState object as the trigger payload. Not exposed as an external webhook endpoint. The Slack approval interaction receiver is a separate inbound webhook endpoint registered in the Slack app Interactivity settings; it resumes the paused Resolution Agent workflow instance by matching the callback_id to the active workflowInstanceId.
Slack interaction receiver
Inbound webhook endpoint on the orchestration layer. Accepts POST from Slack containing the interactive payload JSON. Validates the request using HMAC-SHA256 of the raw body with SLACK_SIGNING_SECRET and the X-Slack-Signature header. Must respond with HTTP 200 within 3 seconds to prevent Slack timeout errors. Resume logic: match payload.callback_id to the paused workflow instance and inject the approval decision.
Credential store structure
See code block below. All secrets are stored at the platform level in the shared credential store, not in individual workflow node configurations. Reference credentials by name in node settings; never paste raw values into node fields.
State persistence
The workflowState object is passed between nodes within each workflow as an in-memory context. For the approval wait step (which may pause for up to 24 hours), the workflowState must be persisted to durable storage (platform database or an external key-value store keyed by workflowInstanceId) so the instance can be resumed after a platform restart.
Logging
All workflow executions must log: trigger source (webhook or poll), shipmentId, exceptionType, resolutionType, approvalDecision, and final status (success or error with error code). Logs must be retained for a minimum of 90 days for audit purposes.
Shared credential store: all keys referenced by name in workflow nodes, never hardcoded
// Credential store contents
// Store all values at platform credential level; reference by key name in node config
SHIPSTATION_API_KEY : string // ShipStation API key
SHIPSTATION_API_SECRET : string // ShipStation API secret
SHIPSTATION_WEBHOOK_SECRET_TOKEN : string // appended to webhook receiver URL as ?token=
SHOPIFY_STORE_DOMAIN : string // e.g. yourcompany.myshopify.com
SHOPIFY_ADMIN_API_TOKEN : string // custom app Admin API access token
SHOPIFY_API_VERSION : string // pinned, e.g. '2024-04'
SHOPIFY_ORDER_VALUE_THRESHOLD : number // approval threshold in USD, business-defined
GORGIAS_BASE_URL : string // e.g. https://yourcompany.gorgias.com
GORGIAS_EMAIL : string // service account email
GORGIAS_API_KEY : string // Gorgias REST API key
GORGIAS_FULFILMENT_QUEUE_ID : integer // pre-fetched view ID, not name string
GORGIAS_TAG_MAP : JSON // {address_issue:'exc-address', ...}
GMAIL_CLIENT_ID : string // Google Cloud OAuth client ID
GMAIL_CLIENT_SECRET : string // Google Cloud OAuth client secret
GMAIL_REFRESH_TOKEN : string // long-lived refresh token
GMAIL_SENDER_ADDRESS : string // authorised sending address
SLACK_BOT_TOKEN : string // xoxb- prefixed bot token
SLACK_SIGNING_SECRET : string // for HMAC-SHA256 request validation
SLACK_APPROVAL_CHANNEL_ID : string // channel or DM for manager approval
SLACK_FULFILMENT_CHANNEL_ID : string // channel for resolution summaries
APPROVAL_TIMEOUT_SECONDS : integer // default 86400 (24 hours)
GOOGLE_SERVICE_ACCOUNT_KEY_JSON : string // base64-encoded service account JSON
SHEETS_SPREADSHEET_ID : string // Google Sheets file ID from URL
SHEETS_LOG_SHEET_NAME : string // default 'Exception Log'
// Carrier exception code mapping (workflow variable, not a secret)
CARRIER_EXCEPTION_CODE_MAP : JSON // {usps:{A1:'address_issue',...}, fedex:{...}, ups:{...}, dhl:{...}}05Error handling and retry logic
Every integration point has a defined failure behaviour. Unhandled exceptions must never fail silently. Any error not matched by the table below must be caught by a global error handler that logs the full error payload, posts an alert to SLACK_FULFILMENT_CHANNEL_ID, and halts the workflow instance without retrying.
Integration
Scenario
Required behaviour
ShipStation webhook
Webhook payload received but signature token does not match
Return HTTP 401 immediately. Do not process the payload. Log the rejected request with the source IP and raw payload hash. Post an amber alert to SLACK_FULFILMENT_CHANNEL_ID. No retry.
ShipStation webhook
Webhook received but orderNumber is missing or malformed
Return HTTP 200 to ShipStation (prevent retry storm). Log the malformed payload. Post an alert to SLACK_FULFILMENT_CHANNEL_ID with the raw payload for manual review. Halt workflow instance.
ShipStation polling fallback
GET /shipments returns 429 (rate limit)
Pause polling for 60 seconds. Retry up to 3 times with exponential backoff (60s, 120s, 240s). If all retries fail, log the failure and skip the current poll cycle. Resume at the next scheduled interval. Do not alert unless 3 consecutive poll cycles fail.
Shopify order lookup
GET /orders returns 404 (order not found for given orderNumber)
Retry once after 30 seconds (orderNumber may not yet be indexed). If second attempt also returns 404, log the shipmentId and orderNumber, post an alert to SLACK_FULFILMENT_CHANNEL_ID flagging the unmatched exception, and halt the workflow instance for manual resolution.
Shopify order lookup
GET /orders returns 429 (rate limit) or 5xx (server error)
Retry up to 3 times with exponential backoff (10s, 30s, 90s). If all retries fail, log the error with the full HTTP response, post an alert to SLACK_FULFILMENT_CHANNEL_ID, and halt the workflow instance. Do not proceed without a valid order record.
Gorgias ticket creation
POST /api/tickets returns 409 (ticket already exists for this order)
Check workflowState for an existing gorgiasTicketId. If found, skip creation and use the existing ticket ID. If no ID is stored, query GET /api/tickets?order_number={orderName} to retrieve the existing ticket ID and store it. Log the duplicate detection.
Gorgias ticket creation
POST /api/tickets returns 401 or 403
Do not retry. Log the authentication failure with the HTTP status and response body. Post a red alert to SLACK_FULFILMENT_CHANNEL_ID. Halt the workflow instance. This indicates a credential or permission issue requiring manual fix before the automation can continue.
Gmail send
Gmail API returns 401 (token expired or revoked)
Attempt token refresh using GMAIL_REFRESH_TOKEN. If refresh succeeds, retry the send once. If refresh fails or the retry also fails with 401, log the error, post a red alert to SLACK_FULFILMENT_CHANNEL_ID, and halt the workflow instance. Flag for manual email send.
Gmail send
Gmail API returns 429 or 500
Retry up to 3 times with backoff (15s, 45s, 120s). If all retries fail, log the error and post an alert. Do not halt the full workflow; continue to Slack and Shopify steps but flag the customer notification as unsent in the Sheets log (Notes column: 'Customer email failed').
Slack approval prompt
No manager response received within APPROVAL_TIMEOUT_SECONDS
After the initial timeout, post a reminder message to SLACK_APPROVAL_CHANNEL_ID and pause for a further 4 hours. If still no response, escalate by posting a red alert to SLACK_FULFILMENT_CHANNEL_ID tagging the manager by Slack user ID, and pause for a further 2 hours. If still no response after a total of 30 hours, log the timeout and halt the workflow instance as 'pending manual approval'. Do not auto-apply any resolution.
Shopify reship or refund
POST to draft_orders or refunds returns 422 (unprocessable entity)
Log the full 422 response body (Shopify returns validation detail). Do not retry automatically. Post a red alert to SLACK_FULFILMENT_CHANNEL_ID with the order name, resolution type attempted, and the Shopify error message for manual fix. Halt the workflow instance at this step; the Gorgias ticket and Gmail send have already completed.
Google Sheets append
Sheets API returns any error (4xx or 5xx)
Retry up to 3 times with backoff (10s, 30s, 60s). If all retries fail, log the row data that failed to append to the workflow execution log so it can be manually entered. Post an amber alert to SLACK_FULFILMENT_CHANNEL_ID. Do not halt the workflow; the resolution in Shopify has already been applied. The audit log gap must be manually corrected.
Global unhandled exception rule: any error not matched by the table above must be caught by the orchestration layer's global error handler. The handler must log the full error object (including stack trace where available), post a red alert to SLACK_FULFILMENT_CHANNEL_ID with the workflow instance ID and the step name where the error occurred, and halt the workflow instance. Unhandled exceptions must never fail silently. Contact support@gofullspec.com if an unhandled exception pattern is encountered during testing or production.
Integration and API SpecPage 4 of 4