FS-DOC-05Technical
Integration and API Spec
Order Processing and Fulfilment
[YourCompany.com] · Fulfilment Department · Prepared by FullSpec · [Today's Date]
This document is the definitive technical reference for every integration point in the Order Processing and Fulfilment automation. It covers the tool inventory, per-tool auth and scope requirements, field mappings between agents, orchestration layout, credential store structure, and error handling behaviour. The FullSpec team uses this spec to configure, validate, and hand over the automation. No integration work should begin until every item in Section 01 has been confirmed against the actual accounts in scope.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agents
Shopify
Order source trigger; fulfilment status and tracking update
OAuth 2.0 / Private App API key
Basic ($29/mo) or above
Agent 1, Agent 2
Cin7
Stock-level lookup per SKU; inventory deduction on dispatch
API key + Account ID (Basic Auth over HTTPS)
Standard (includes API access)
Agent 1
Xero
Sales invoice creation with line items from Shopify order
OAuth 2.0 (PKCE flow)
Starter or above
Agent 2
StarShipIt
Courier booking, label generation, tracking number retrieval
API key (header-based)
Any paid plan with API access
Agent 2
Gmail
Outbound shipping confirmation email to customer
OAuth 2.0 (Google Workspace or personal)
Free (Google account required)
Agent 2
Slack
Stock-shortfall alert (Agent 1); dispatch notification (Agent 2)
OAuth 2.0 Bot Token (Slack App)
Free or above (Bot API included)
Agent 1, Agent 2
Orchestration layer
Workflow execution, scheduling, credential storage, retry logic
Platform-managed (internal service account)
Determined at build selection
All agents
Before you connect anything: provision sandbox or test credentials for every tool listed above and validate each connection in a non-production environment before any production credentials are loaded. Shopify has a Partners developer store option; Cin7 provides a sandbox on request; Xero Demo Company is available to all accounts; StarShipIt supports a test mode via the API. Gmail and Slack connections should be tested against a dedicated internal address and a private channel respectively.
Integration and API SpecPage 1 of 4
FS-DOC-05Technical
02Per-tool integration detail
Shopify
Acts as the automation trigger (incoming paid order webhook) and as a write target for fulfilment status and tracking number. Used by Agent 1 for order ingestion and Agent 2 for marking the order fulfilled.
Auth method
OAuth 2.0 via Custom App in the Shopify Admin, or a Private App API key for stores on legacy plans. Store the access token in the credential store as SHOPIFY_ACCESS_TOKEN. The shop domain is stored as SHOPIFY_STORE_DOMAIN.
Required scopes
read_orders, write_orders, read_fulfillments, write_fulfillments, read_products, read_inventory
Webhook setup
Register a webhook for the topic orders/paid at the endpoint exposed by the orchestration layer. Validate every inbound request using the X-Shopify-Hmac-SHA256 header against the shared secret stored as SHOPIFY_WEBHOOK_SECRET. Reject any request where the HMAC does not match before processing.
Required configuration
Shopify fulfillment location ID must be resolved at build time and stored as SHOPIFY_LOCATION_ID. Do not hardcode. Verify that Shopify's own fulfilment notification emails are disabled or suppressed to prevent duplicate customer emails alongside the Gmail step.
Rate limits
REST Admin API: 2 requests/second (leaky bucket, burst to 40). At 280 orders/month (~9.3/day), peak throughput is well within limits. No throttling wrapper is required at current volume, but the orchestration layer must respect the Retry-After header on any 429 response.
Constraints
The orders/paid webhook fires only for orders with financial_status = paid. Test with orders in pending status to confirm they are correctly excluded. Multi-location inventory is not in scope for this build; a single fulfilment location is assumed.
// Inbound webhook payload (key fields consumed)
order.id -> internal order_id
order.order_number -> human-readable reference
order.line_items[] -> sku, quantity, title, price
order.shipping_address -> name, address1, address2, city, zip, country_code, phone
order.customer.email -> recipient address for Gmail step
order.customer.first_name -> personalisation token in email template
order.total_price -> invoice total passed to Xero
// Write back (Agent 2)
POST /orders/{order_id}/fulfillments -> fulfillment.tracking_number, fulfillment.tracking_url, fulfillment.tracking_companyCin7
Queried by Agent 1 to confirm on-hand stock for each SKU in the incoming order. Cin7 does not require a write operation in this automation; stock deduction occurs automatically via Cin7's own Shopify integration or is handled by the existing Cin7 workflow. If no native sync exists, a manual deduction step remains with the coordinator.
Auth method
HTTP Basic Auth: Base64-encode AccountID:APIKey and pass in the Authorization header. Store as CIN7_ACCOUNT_ID and CIN7_API_KEY in the credential store.
Required scopes
Cin7 Core API does not use OAuth scopes. The API key must belong to a user role with at minimum: Products (read), Stock (read). Confirm with the Cin7 account admin that the key is not restricted to a single branch or warehouse if multi-location is in use.
Webhook setup
No inbound webhook required. Agent 1 performs a synchronous GET request to the stock endpoint immediately after the Shopify webhook fires.
Required configuration
Store the target branch or warehouse ID as CIN7_BRANCH_ID if stock checking is branch-specific. The SKU field in Cin7 must match the variant SKU field in Shopify exactly; this mapping must be validated during the stack audit before go-live.
Rate limits
Cin7 Core API: 100 requests/minute per API key. Each order triggers one GET per unique SKU (typically 1 to 5 per order). At 280 orders/month, peak is well under the limit. No throttling needed at current volume.
Constraints
Stock data accuracy is a hard dependency. If Cin7 records are out of date, the stock-check logic will produce false negatives (blocking valid orders) or false positives (shipping from unavailable stock). A data-quality review of Cin7 is required before go-live. See implementation notes in the Developer Handover Pack.
// GET /api/v1/stock?where=SKU='{sku}'&fields=onHand,available
// Response fields consumed
product.sku -> matched against order line_items[].sku
product.available -> compared against line_items[].quantity
// Output to orchestration layer
stock_check_result -> 'pass' if all SKUs sufficient, 'fail' with shortfall_detail[] if notXero
Agent 2 creates a sales invoice in Xero immediately after Agent 1 confirms stock availability. Line items, totals, and customer data are mapped from the Shopify order payload. No manual re-entry is required.
Auth method
OAuth 2.0 with PKCE. The orchestration layer must store the refresh token as XERO_REFRESH_TOKEN and the tenant (organisation) ID as XERO_TENANT_ID. Access tokens expire after 30 minutes; implement automatic token refresh using the refresh token before each API call.
Required scopes
openid, profile, email, accounting.transactions, accounting.contacts, offline_access
Webhook setup
No inbound webhook required from Xero for this automation. All calls are outbound from the orchestration layer to the Xero API.
Required configuration
Store the Xero Account Code for sales revenue as XERO_SALES_ACCOUNT_CODE (e.g. 200). Store the default Tax Type string as XERO_TAX_TYPE (e.g. OUTPUT or NONE depending on the account configuration). The invoice Type must be set to ACCREC. Do not hardcode account codes; retrieve from the credential store at runtime.
Rate limits
Xero API: 60 calls/minute and 5,000 calls/day per organisation. At 280 orders/month (~9.3/day), a single POST per order is comfortably within daily and per-minute limits. No throttling required at current volume.
Constraints
The Xero contact must exist before the invoice is created. The automation should perform a GET /Contacts?where=EmailAddress='{email}' lookup first. If no matching contact exists, create one using the Shopify customer fields before posting the invoice. Duplicate contact creation must be guarded against.
// POST /api.xro/2.0/Invoices
// Request fields mapped from Shopify payload
Contact.EmailAddress <- order.customer.email
Contact.Name <- order.shipping_address.name
LineItems[].Description <- line_items[].title
LineItems[].Quantity <- line_items[].quantity
LineItems[].UnitAmount <- line_items[].price
LineItems[].AccountCode <- XERO_SALES_ACCOUNT_CODE
LineItems[].TaxType <- XERO_TAX_TYPE
Reference <- order.order_number
// Response fields stored
invoice.InvoiceID -> logged to run record for audit
StarShipIt
Agent 2 pushes the validated order to StarShipIt to book the appropriate courier service, generate the shipping label, and retrieve the tracking number. Courier selection is rule-based within StarShipIt and must be configured before go-live.
Auth method
API key passed in the request header as Subscription-Key: {key}. Store as STARSHIPIT_API_KEY. No OAuth flow required.
Required scopes
StarShipIt uses key-level permissions rather than OAuth scopes. The key must have Orders (create, read), Labels (generate), and Tracking (read) permissions enabled in the StarShipIt account settings.
Webhook setup
Optional: configure a StarShipIt outbound webhook for the shipment.dispatched event to push tracking updates back to the orchestration layer as a redundancy check. Store the endpoint URL in the StarShipIt dashboard. The primary flow uses a synchronous POST and then GET to retrieve the tracking number without a webhook dependency.
Required configuration
Courier service rules (carrier selection by weight, destination, or product category) must be documented and configured inside StarShipIt before the build starts. Store the default carrier code as STARSHIPIT_DEFAULT_CARRIER if a fallback is needed. The sender address must be confirmed and set as the default in the StarShipIt account; do not pass it dynamically unless multiple dispatch locations are in scope.
Rate limits
StarShipIt API: 500 requests/minute. At 280 orders/month, this limit is not a concern. No throttling required.
Constraints
StarShipIt requires a valid delivery address for every booking. If the Shopify address is incomplete or unrecognised, StarShipIt will return a 400 or 422 error. Address validation logic must be implemented before the StarShipIt call and must route incomplete addresses to the manual exception flow via Slack. Label format (PDF, ZPL) must be confirmed against the warehouse printer setup before go-live.
// POST /api/orders
// Request fields mapped from Shopify payload
order.order_date <- order webhook received_at timestamp
order.reference <- order.order_number
order.destination.name <- order.shipping_address.name
order.destination.address <- order.shipping_address.address1 + address2
order.destination.suburb <- order.shipping_address.city
order.destination.postcode<- order.shipping_address.zip
order.destination.country <- order.shipping_address.country_code
order.destination.phone <- order.shipping_address.phone
order.products[].sku <- line_items[].sku
order.products[].qty <- line_items[].quantity
// GET /api/orders/{order_id}/labels -> label binary (PDF or ZPL)
// GET /api/orders/{order_id} -> shipment.tracking_number, shipment.carrier_name
tracking_number -> passed to Shopify fulfillment and Gmail template
carrier_name -> passed to Gmail templateGmail
Agent 2 sends a personalised shipping confirmation email to the customer after the tracking number has been retrieved from StarShipIt. A pre-approved template is used; no freeform composition occurs at runtime.
Auth method
OAuth 2.0. Store the refresh token as GMAIL_REFRESH_TOKEN and the sending address as GMAIL_SENDER_ADDRESS. Use the Gmail API (not SMTP) for send operations to maintain token-based auth consistency. Access tokens expire after 1 hour; implement automatic refresh.
Required scopes
https://www.googleapis.com/auth/gmail.send
Webhook setup
No inbound webhook required. All Gmail calls in this automation are outbound (send only).
Required configuration
The email template must be stored in the orchestration layer's template store (not in Gmail Drafts) as a parameterised HTML string. Template placeholders are: {{customer_first_name}}, {{order_number}}, {{tracking_number}}, {{carrier_name}}, {{tracking_url}}. The template ID is stored as GMAIL_TEMPLATE_ID. Confirm with the business owner that Shopify's own fulfilment email notifications are turned off before activating this step.
Rate limits
Gmail API sending quota: 500 messages/day for standard Google accounts; 2,000/day for Google Workspace. At 280 orders/month (~9.3/day), well within any tier. No throttling needed.
Constraints
The Gmail OAuth token is tied to a specific Google account. If that account is deactivated or the token is revoked, the send step will fail silently unless a health-check alert is in place. A weekly token validity check is recommended as part of the monitoring runbook.
// POST https://gmail.googleapis.com/gmail/v1/users/me/messages/send
// MIME message constructed at runtime
To <- order.customer.email
From <- GMAIL_SENDER_ADDRESS
Subject <- 'Your order {{order_number}} has shipped'
Body (HTML) <- GMAIL_TEMPLATE_ID rendered with:
{{customer_first_name}} <- order.customer.first_name
{{order_number}} <- order.order_number
{{tracking_number}} <- starshipit.tracking_number
{{carrier_name}} <- starshipit.carrier_name
{{tracking_url}} <- starshipit.tracking_urlSlack
Used by Agent 1 to send a stock-shortfall alert to the fulfilment team when one or more SKUs cannot be fulfilled. Used by Agent 2 to post a dispatch confirmation after the order is fully processed. Both messages use a Slack Bot Token.
Auth method
OAuth 2.0 Bot Token. Create a Slack App in the target workspace, grant the required scopes, install to the workspace, and store the Bot Token as SLACK_BOT_TOKEN. Do not use a legacy incoming webhook URL; use the chat.postMessage API method for consistency and error visibility.
Required scopes
chat:write, chat:write.public (if posting to channels the bot has not been explicitly invited to)
Webhook setup
No inbound webhook required. Both calls are outbound from the orchestration layer using chat.postMessage.
Required configuration
Store the stock-alert channel ID as SLACK_ALERT_CHANNEL_ID and the dispatch notification channel ID as SLACK_DISPATCH_CHANNEL_ID. Use channel IDs (e.g. C04XXXXXXX), not channel names, to avoid breakage if channels are renamed. The bot must be invited to both channels before go-live.
Rate limits
Slack Web API: Tier 3 methods (chat.postMessage) allow 50+ requests/minute. At fewer than 10 messages/day at current volume, this is not a constraint. No throttling required.
Constraints
If a channel is archived or the bot is removed, chat.postMessage will return channel_not_found or not_in_channel. These errors must trigger an alert to support@gofullspec.com and must not silently drop the message. The stock-shortfall alert in particular must never be swallowed because it is the only human-intervention signal in the automated flow.
// Agent 1: Stock shortfall alert
POST https://slack.com/api/chat.postMessage
channel <- SLACK_ALERT_CHANNEL_ID
text <- ':warning: Stock shortfall on order {{order_number}}'
blocks[].shortfall_detail <- sku, ordered_qty, available_qty for each failing SKU
// Agent 2: Dispatch confirmation
POST https://slack.com/api/chat.postMessage
channel <- SLACK_DISPATCH_CHANNEL_ID
text <- ':white_check_mark: Dispatched: Order {{order_number}} | {{customer_name}} | {{carrier_name}} | {{tracking_number}}'Integration 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. All field names are exact as they appear in the respective API responses and request bodies. Mappings must be validated against live sandbox responses during the build phase before production credentials are connected.
Handoff 1: Shopify order webhook to Agent 1 (Order Intake Agent) internal record
Source tool
Source field
Destination tool
Destination field
Shopify
order.id
Internal record
order_id
Shopify
order.order_number
Internal record
order_number
Shopify
order.line_items[].sku
Internal record
line_items[].sku
Shopify
order.line_items[].quantity
Internal record
line_items[].quantity
Shopify
order.line_items[].title
Internal record
line_items[].title
Shopify
order.line_items[].price
Internal record
line_items[].unit_price
Shopify
order.total_price
Internal record
order_total
Shopify
order.customer.email
Internal record
customer_email
Shopify
order.customer.first_name
Internal record
customer_first_name
Shopify
order.shipping_address.name
Internal record
shipping_name
Shopify
order.shipping_address.address1
Internal record
shipping_address1
Shopify
order.shipping_address.address2
Internal record
shipping_address2
Shopify
order.shipping_address.city
Internal record
shipping_city
Shopify
order.shipping_address.zip
Internal record
shipping_postcode
Shopify
order.shipping_address.country_code
Internal record
shipping_country_code
Shopify
order.shipping_address.phone
Internal record
shipping_phone
Handoff 2: Agent 1 internal record to Cin7 stock check
Source tool
Source field
Destination tool
Destination field
Internal record
line_items[].sku
Cin7
GET /stock?where=SKU='{sku}'
Internal record
line_items[].quantity
Internal comparison
compared against Cin7 product.available
Handoff 3: Agent 1 to Agent 2 (Fulfilment Dispatch Agent) on stock-pass result
Source tool
Source field
Destination tool
Destination field
Internal record
order_id
Agent 2 payload
order_id
Internal record
order_number
Agent 2 payload
order_number
Internal record
order_total
Agent 2 payload
order_total
Internal record
customer_email
Agent 2 payload
customer_email
Internal record
customer_first_name
Agent 2 payload
customer_first_name
Internal record
shipping_name
Agent 2 payload
shipping_name
Internal record
shipping_address1
Agent 2 payload
shipping_address1
Internal record
shipping_address2
Agent 2 payload
shipping_address2
Internal record
shipping_city
Agent 2 payload
shipping_city
Internal record
shipping_postcode
Agent 2 payload
shipping_postcode
Internal record
shipping_country_code
Agent 2 payload
shipping_country_code
Internal record
shipping_phone
Agent 2 payload
shipping_phone
Internal record
line_items[]
Agent 2 payload
line_items[]
Handoff 4: Agent 2 internal payload to Xero invoice creation
Source tool
Source field
Destination tool
Destination field
Agent 2 payload
customer_email
Xero
Contact.EmailAddress
Agent 2 payload
shipping_name
Xero
Contact.Name
Agent 2 payload
order_number
Xero
Invoice.Reference
Agent 2 payload
line_items[].title
Xero
LineItems[].Description
Agent 2 payload
line_items[].quantity
Xero
LineItems[].Quantity
Agent 2 payload
line_items[].unit_price
Xero
LineItems[].UnitAmount
Credential store
XERO_SALES_ACCOUNT_CODE
Xero
LineItems[].AccountCode
Credential store
XERO_TAX_TYPE
Xero
LineItems[].TaxType
Handoff 5: Agent 2 internal payload to StarShipIt order creation
Source tool
Source field
Destination tool
Destination field
Agent 2 payload
order_number
StarShipIt
order.reference
Agent 2 payload
shipping_name
StarShipIt
order.destination.name
Agent 2 payload
shipping_address1
StarShipIt
order.destination.address
Agent 2 payload
shipping_address2
StarShipIt
order.destination.address2
Agent 2 payload
shipping_city
StarShipIt
order.destination.suburb
Agent 2 payload
shipping_postcode
StarShipIt
order.destination.postcode
Agent 2 payload
shipping_country_code
StarShipIt
order.destination.country
Agent 2 payload
shipping_phone
StarShipIt
order.destination.phone
Agent 2 payload
line_items[].sku
StarShipIt
order.products[].sku
Agent 2 payload
line_items[].quantity
StarShipIt
order.products[].qty
Handoff 6: StarShipIt response to Shopify fulfillment write and Gmail template
Source tool
Source field
Destination tool
Destination field
StarShipIt
shipment.tracking_number
Shopify
fulfillment.tracking_number
StarShipIt
shipment.tracking_url
Shopify
fulfillment.tracking_url
StarShipIt
shipment.carrier_name
Shopify
fulfillment.tracking_company
StarShipIt
shipment.tracking_number
Gmail template
{{tracking_number}}
StarShipIt
shipment.carrier_name
Gmail template
{{carrier_name}}
StarShipIt
shipment.tracking_url
Gmail template
{{tracking_url}}
Agent 2 payload
customer_first_name
Gmail template
{{customer_first_name}}
Agent 2 payload
order_number
Gmail template
{{order_number}}
Integration and API SpecPage 3 of 4
FS-DOC-05Technical
04Build stack and orchestration
Orchestration layout
Two discrete workflows are implemented: one per agent. Workflow 1 corresponds to the Order Intake Agent; Workflow 2 corresponds to the Fulfilment Dispatch Agent. The two workflows communicate via an internal run-record or shared data store within the orchestration layer, not via a direct API call between agents. Both workflows share a single credential store scoped to this automation.
Agent 1 trigger mechanism
Webhook (push). Shopify fires an orders/paid webhook to the orchestration layer endpoint immediately on payment confirmation. The endpoint validates the X-Shopify-Hmac-SHA256 signature using SHOPIFY_WEBHOOK_SECRET before any processing begins. Invalid signatures return HTTP 401 and the run is discarded without logging customer data.
Agent 2 trigger mechanism
Internal event (push from Agent 1). Agent 1 writes a validated order record to the shared run store and emits an internal trigger event that activates Workflow 2. No polling is used. If Agent 2 does not acknowledge the trigger within 60 seconds, the event is re-queued once before escalating to the error handler.
Webhook signature validation
Compute HMAC-SHA256 of the raw request body using SHOPIFY_WEBHOOK_SECRET. Compare against the value in the X-Shopify-Hmac-SHA256 header using a constant-time comparison function to prevent timing attacks. Do not parse the body before validation.
Credential store scope
All secrets are stored in the orchestration layer's built-in encrypted credential store. No credential is written to a log file, run record, or notification payload at any point. Credentials are injected at runtime via environment references only.
Retry and backoff policy
All outbound API calls use exponential backoff: first retry after 30 seconds, second retry after 2 minutes, third retry after 10 minutes. After three failures the run is marked as error and a Slack alert is sent to SLACK_ALERT_CHANNEL_ID. See Section 05 for per-integration failure behaviours.
Monitoring
Each workflow emits a structured run log entry on completion (success or error). A daily summary of run counts, error rates, and average duration is posted to SLACK_DISPATCH_CHANNEL_ID. Failed runs that are not retried successfully must trigger an alert to support@gofullspec.com within 15 minutes of the third retry failure.
Credential store contents (all values injected at runtime from the encrypted store):
Credential store: inject via environment reference only. Never log or expose these values.
// Credential store: Order Processing and Fulfilment automation
// All values are environment references. No plaintext secrets in workflow code.
SHOPIFY_STORE_DOMAIN // e.g. yourstore.myshopify.com
SHOPIFY_ACCESS_TOKEN // Private App or Custom App OAuth token
SHOPIFY_WEBHOOK_SECRET // Shared secret for HMAC-SHA256 validation
SHOPIFY_LOCATION_ID // Fulfilment location ID (resolved at build time)
CIN7_ACCOUNT_ID // Cin7 account identifier for Basic Auth
CIN7_API_KEY // Cin7 API key for Basic Auth
CIN7_BRANCH_ID // Target branch/warehouse ID for stock lookup
XERO_CLIENT_ID // OAuth 2.0 application client ID
XERO_CLIENT_SECRET // OAuth 2.0 application client secret
XERO_REFRESH_TOKEN // Long-lived refresh token; auto-rotated on use
XERO_TENANT_ID // Xero organisation (tenant) ID
XERO_SALES_ACCOUNT_CODE // Chart of accounts code for sales revenue (e.g. 200)
XERO_TAX_TYPE // Tax type string (e.g. OUTPUT, NONE)
STARSHIPIT_API_KEY // StarShipIt API subscription key (header-based)
STARSHIPIT_DEFAULT_CARRIER // Fallback carrier code if rules do not match
GMAIL_REFRESH_TOKEN // Google OAuth 2.0 refresh token for sending account
GMAIL_SENDER_ADDRESS // Verified sending email address
GMAIL_TEMPLATE_ID // Internal reference to the parameterised HTML template
SLACK_BOT_TOKEN // Slack App Bot Token (xoxb-...)
SLACK_ALERT_CHANNEL_ID // Channel ID for stock shortfall and error alerts
SLACK_DISPATCH_CHANNEL_ID // Channel ID for dispatch confirmations and run summaries
05Error handling and retry logic
Unhandled exceptions must never fail silently. Every integration point below has a defined failure path. If an error is not matched by a handler in this table, the run must be halted, logged as 'unhandled exception', and an alert sent to SLACK_ALERT_CHANNEL_ID and support@gofullspec.com within 15 minutes.
Integration
Scenario
Required behaviour
Shopify (inbound)
HMAC signature validation fails on inbound webhook
Return HTTP 401 immediately. Discard payload. Do not process. Log the event with the request timestamp and originating IP. Alert SLACK_ALERT_CHANNEL_ID if more than 3 failures occur within 5 minutes (potential spoofing).
Shopify (inbound)
Duplicate webhook delivery (Shopify may retry on non-200 response)
Implement idempotency check on order.id before processing. If order_id already exists in the run store with status 'complete', return HTTP 200 and skip processing. Log as duplicate.
Cin7 (read)
Cin7 API returns 401 or 403 (auth failure)
Halt Agent 1. Do not pass order downstream. Alert SLACK_ALERT_CHANNEL_ID with order_number and error detail. Retry after 30 seconds once. If second attempt fails, escalate to support@gofullspec.com and mark run as 'auth error pending review'.
Cin7 (read)
Cin7 API returns 200 but SKU not found in response
Treat as stock-unknown. Route order to manual review via Slack alert (same path as stock shortfall). Do not dispatch the order. Log which SKU was unresolvable.
Cin7 (read)
Stock check returns shortfall for one or more SKUs
Send structured Slack alert to SLACK_ALERT_CHANNEL_ID with order_number, customer_email, and shortfall_detail[]. Mark run as 'held pending manual review'. Do not proceed to Agent 2. Update run record status to 'stock hold'.
Xero (write)
Xero returns 401 (token expired or revoked)
Attempt one automatic token refresh using XERO_REFRESH_TOKEN. If refresh succeeds, retry the invoice POST immediately. If refresh fails, halt Agent 2, alert SLACK_ALERT_CHANNEL_ID and support@gofullspec.com. Do not proceed to StarShipIt.
Xero (write)
Xero returns 400 (validation error, e.g. missing contact or bad account code)
Log the full error response body. Alert SLACK_ALERT_CHANNEL_ID with order_number and validation detail. Do not retry automatically. Require manual correction of the Xero configuration by the FullSpec team before the run is replayed.
StarShipIt (write)
StarShipIt returns 422 (invalid or incomplete address)
Halt Agent 2 at the StarShipIt step. Do not mark Shopify fulfilled. Send Slack alert to SLACK_ALERT_CHANNEL_ID with order_number and address fields for manual correction. Retry the step once the address is confirmed by the fulfilment coordinator.
StarShipIt (write)
StarShipIt API returns 500 or times out
Retry with exponential backoff: 30 seconds, then 2 minutes, then 10 minutes. After three failures, halt Agent 2 and alert SLACK_ALERT_CHANNEL_ID. Xero invoice already created at this point must be voided manually; include invoice ID in the alert message.
Shopify (write)
Fulfillment POST fails (e.g. order already fulfilled or 404)
Log the error response. If the order is already fulfilled (conflict), check whether tracking number matches. If tracking matches, treat as idempotent success. If tracking differs, alert SLACK_ALERT_CHANNEL_ID for manual review. Never overwrite an existing fulfillment silently.
Gmail (send)
Gmail API returns 401 (token expired or revoked)
Attempt one automatic token refresh using GMAIL_REFRESH_TOKEN. If refresh succeeds, retry the send immediately. If refresh fails, log the failure, alert SLACK_ALERT_CHANNEL_ID, and queue the email for manual send by the fulfilment coordinator. Shopify fulfillment and invoice steps are already complete and must not be rolled back.
Slack (write)
Slack chat.postMessage returns channel_not_found or not_in_channel
Log the error. If the failing message is the stock-shortfall alert (Agent 1), escalate immediately to support@gofullspec.com by email because this is the only human-intervention signal in the flow and must not be dropped. If the failing message is the dispatch notification (Agent 2), log and retry once; the dispatch has already completed so this is a non-critical notification failure.
Questions about this specification should be directed to support@gofullspec.com. Do not attempt to resolve credential, scope, or rate-limit issues by modifying production credentials without first consulting the FullSpec team. All changes to the credential store or webhook endpoints must be logged in the change record for this automation.
Integration and API SpecPage 4 of 4