FS-DOC-05Technical
Integration and API Spec
Shipping and Tracking Notification
[YourCompany.com] · Fulfilment Department · Prepared by FullSpec · [Today's Date]
This document defines every integration point, authentication method, required scope, field mapping, and error-handling rule for the Shipping and Tracking Notification automation. It is written for the FullSpec build team and covers ShipStation, Shopify, Klaviyo, Google Sheets, Slack, and the orchestration layer. Each section contains the exact configuration values, credential store references, and retry behaviours required before any connection is promoted to production.
01Tool inventory
Tool
Role in automation
Auth method
Min plan required
Used by agent(s)
ShipStation
Shipment trigger and tracking data source
API Key + API Secret (Basic Auth over HTTPS)
Starter ($9.99/mo) or above for webhook support
Agent 1: Shipment Event Listener
Shopify
Customer and order detail lookup
OAuth 2.0 (custom app) or Admin API token
Basic Shopify or above; Admin API access required
Agent 1: Shipment Event Listener
Klaviyo
Branded transactional email delivery
Private API Key (Bearer token)
Free tier for testing; paid plan for production send volume
Agent 2: Notification Dispatch Agent
Google Sheets
Notification audit log
OAuth 2.0 via Google service account
Any Google Workspace or personal Google account
Agent 2: Notification Dispatch Agent
Slack
Daily dispatch summary alerts
Bot token (OAuth 2.0 via Slack app install)
Free tier sufficient; channel must exist before build
Agent 2: Notification Dispatch Agent
Orchestration layer
Connects all tools; hosts workflows, credential store, and retry logic
Internal platform credentials; per-tool credentials stored in encrypted credential store
Platform subscription at $40/month (Standard build)
Both agents
Before you connect anything: create sandbox or test credentials for every tool listed above and validate each connection in a non-production environment before supplying live API keys. For ShipStation, use a test webhook endpoint (e.g. a request-bin URL) to confirm payload shape before pointing at the production workflow. For Klaviyo, target a seed-list profile during integration testing so no real customers receive test emails. Promote to production credentials only after all per-tool tests in Section 02 pass.
02Per-tool integration detail
ShipStation
Primary trigger source. The automation detects order status changes to 'Shipped' with a non-null tracking number, either via webhook push or polling fallback. All tracking data originates here.
Auth method
HTTP Basic Auth: Base64-encode 'API_KEY:API_SECRET' and pass in the Authorization header on every request. Credentials stored in the credential store as SHIPSTATION_API_KEY and SHIPSTATION_API_SECRET.
Required scopes
orders:read, shipments:read, webhooks:write (webhook registration), webhooks:read (webhook listing). ShipStation does not use OAuth scopes in the traditional sense; access level is governed by the API key tier associated with the account plan.
Webhook setup
Register a webhook via POST /webhooks/subscribe with event type 'SHIP_NOTIFY'. The target URL must be the orchestration layer's inbound webhook endpoint. Store the registered webhook ID in the credential store as SHIPSTATION_WEBHOOK_ID. ShipStation signs payloads with an X-ShipStation-Hmac-SHA256 header; the orchestration layer must validate this signature using SHIPSTATION_WEBHOOK_SECRET before processing any payload. Polling fallback: GET /shipments?shipDate={today}&page=1&pageSize=500 on a 10-minute interval when webhook delivery is unavailable.
Required configuration
Store base tracking URLs per carrier in the credential store (see Section 04). Pipeline stage name must match exactly: 'Shipped'. Carrier code map must include all active carriers (UPS, USPS, FedEx, DHL at minimum) before go-live.
Rate limits
ShipStation enforces 40 requests per minute per API key. At ~100 shipments/week (~15/day peak), the automation will generate at most 30 API calls/day across trigger detection and data fetch. No throttling logic is required at current volume; however, implement a 1.5-second inter-request delay as a precaution during any bulk backfill operation.
Constraints
Webhook retries from ShipStation are not guaranteed; the polling fallback is mandatory. Payload does not always include the full customer record; the Shopify lookup in Agent 1 is required. Tracking number field may be null for some carriers until the label is scanned; do not pass null values downstream.
// Webhook payload (abbreviated)
{
"resource_url": "https://ssapi.shipstation.com/shipments?shipmentId=98765432",
"resource_type": "SHIP_NOTIFY"
}
// Follow-up GET /shipments/{shipmentId} response fields used:
shipmentId -> internal reference
orderNumber -> maps to Shopify order_name
trackingNumber -> carrier tracking number
carrierCode -> used to build tracking URL
shipDate -> ISO 8601 date string
// Output passed to Agent 1 internal state:
{ shipmentId, orderNumber, trackingNumber, carrierCode, shipDate }Shopify
Used by Agent 1 to fetch the full customer and order record corresponding to the ShipStation order number. Provides customer email, name, and order details for downstream use.
Auth method
Admin REST API access token. Stored in the credential store as SHOPIFY_ACCESS_TOKEN and SHOPIFY_STORE_DOMAIN (e.g. yourcompany.myshopify.com). For custom app installs, use the access token issued at app creation; do not use the legacy private app password.
Required scopes
read_orders, read_customers. These scopes are set during custom app configuration in the Shopify admin under Apps > Develop apps. No write scopes are required for this integration.
Webhook/trigger setup
Shopify is not a trigger in this automation; it is called on demand by Agent 1 after a ShipStation event fires. No inbound webhook registration is needed.
Required configuration
Endpoint pattern: GET https://{SHOPIFY_STORE_DOMAIN}/admin/api/2024-01/orders.json?name={orderNumber}&status=any. The order name field in Shopify (e.g. #1042) must match the orderNumber value from ShipStation. If the store uses a custom order prefix, store that prefix mapping in the credential store as SHOPIFY_ORDER_PREFIX.
Rate limits
Shopify REST Admin API allows 40 requests per app per store per minute (2 requests/second with burst to 40). At ~15 shipments/day, this automation makes at most 15 order lookup calls/day. No throttling required; standard rate limit headers (X-Shopify-Shop-Api-Call-Limit) should be logged but no backoff is needed at current volume.
Constraints
If a Shopify order cannot be matched by order number, the workflow must route to the manual review branch (same path as missing email). The customer email field (email) must be non-null and must pass a basic RFC 5321 format check before being passed to Klaviyo. Guest checkout orders may have a null customer.id; use the top-level email field directly in that case.
// GET /admin/api/2024-01/orders.json?name={orderNumber}&status=any
// Fields extracted from response orders[0]:
orders[0].name -> order_number (display)
orders[0].email -> customer_email
orders[0].shipping_address.first_name -> customer_first_name
orders[0].shipping_address.last_name -> customer_last_name
orders[0].id -> shopify_order_id (internal)
// Output assembled by Agent 1:
{
order_number: "#1042",
customer_email: "jane@example.com",
customer_first_name: "Jane",
tracking_number: "1Z999AA10123456784",
carrier_code: "ups",
tracking_url: "https://www.ups.com/track?tracknum=1Z999AA10123456784",
ship_date: "2024-05-01"
}Integration and API SpecPage 1 of 4
FS-DOC-05Technical
Klaviyo
Receives the assembled shipment data object from Agent 2 and triggers a pre-built transactional flow. Klaviyo owns the email template design and suppression list management. The automation only calls the event track endpoint.
Auth method
Private API Key passed as a Bearer token in the Authorization header. Stored in credential store as KLAVIYO_PRIVATE_API_KEY. The public API key (site ID) is not required for server-side event tracking.
Required scopes
Events: Write (to post the tracking event that triggers the flow), Profiles: Write (to create or update the profile if new), Lists: Read (optional, for suppression checking). Scopes are set when generating the private API key in Klaviyo under Account > Settings > API Keys.
Webhook/trigger setup
The automation posts a custom event to the Klaviyo Events API (POST https://a.klaviyo.com/api/events/). The event name must match the flow trigger exactly: 'Shipment Tracked'. The Klaviyo flow must be pre-built with a metric trigger set to 'Shipment Tracked' and the flow configured as a transactional flow (bypass unsubscribe). Store the Klaviyo flow ID in the credential store as KLAVIYO_FLOW_ID for reference validation.
Required configuration
Template placeholders that must resolve: {{ first_name }}, {{ order_number }}, {{ tracking_url }}, {{ carrier_name }}, {{ ship_date }}. The Klaviyo template must be approved and set to 'Live' status before go-live. Suppression list and consent conditions are managed entirely within Klaviyo; the automation does not replicate that logic. KLAVIYO_FLOW_ID and KLAVIYO_LIST_ID (seed list for testing) stored in credential store.
Rate limits
Klaviyo Events API: 75 requests per second per account. At ~15 events/day, no throttling is needed. Implement exponential backoff only on HTTP 429 responses (see Section 05).
Constraints
The automation must not send to an email address that has failed an RFC 5321 format check. Klaviyo will reject malformed emails with a 400 error; this must be caught and routed to the manual review queue. The 'transactional' flag on the flow bypasses marketing unsubscribes but not hard suppression; do not attempt to override Klaviyo suppression logic in the automation layer.
// POST https://a.klaviyo.com/api/events/
// Headers: Authorization: Klaviyo-API-Key {KLAVIYO_PRIVATE_API_KEY}
// revision: 2024-02-15
{
"data": {
"type": "event",
"attributes": {
"metric": { "data": { "type": "metric", "attributes": { "name": "Shipment Tracked" } } },
"profile": { "data": { "type": "profile", "attributes": { "email": "{customer_email}" } } },
"properties": {
"first_name": "{customer_first_name}",
"order_number": "{order_number}",
"tracking_url": "{tracking_url}",
"carrier_name": "{carrier_name}",
"ship_date": "{ship_date}"
},
"time": "{iso8601_timestamp}"
}
}
}
// Success response: HTTP 202 Accepted
// Failure responses: 400 (bad payload), 429 (rate limited), 5xx (Klaviyo error)Google Sheets
Receives a log record from Agent 2 after each successful or failed notification attempt. Appends one row per shipment event to the fulfilment tracker sheet.
Auth method
OAuth 2.0 via Google service account. Create a service account in Google Cloud Console, download the JSON key file, and store the contents as GOOGLE_SERVICE_ACCOUNT_JSON in the credential store. Share the target spreadsheet with the service account email (Editor permission).
Required scopes
https://www.googleapis.com/auth/spreadsheets (read and append rows). No Drive scope is required if the Spreadsheet ID is hardcoded by reference; store the ID as SHEETS_SPREADSHEET_ID in the credential store, never in workflow logic.
Webhook/trigger setup
Google Sheets is not a trigger in this automation. It is written to on demand by Agent 2 using the Sheets REST API: POST https://sheets.googleapis.com/v4/spreadsheets/{SHEETS_SPREADSHEET_ID}/values/{SHEETS_TAB_NAME}!A:J:append?valueInputOption=USER_ENTERED.
Required configuration
Sheet tab name stored as SHEETS_TAB_NAME (default: 'Notification Log'). Column structure must be pre-configured in the sheet before go-live: A=Timestamp, B=Order Number, C=Customer Email, D=Carrier, E=Tracking Number, F=Tracking URL, G=Klaviyo Status, H=Ship Date, I=Agent Run ID, J=Notes. Row 1 must contain these headers; the append call targets row 2 onwards.
Rate limits
Google Sheets API: 300 read and 300 write requests per minute per project. At ~15 appends/day, no throttling is required. Implement a single retry with 5-second delay on HTTP 429.
Constraints
Do not store raw API keys or credentials in any sheet cell. The service account must not be granted Owner access; Editor is sufficient. If the sheet is moved or renamed, SHEETS_SPREADSHEET_ID and SHEETS_TAB_NAME must be updated in the credential store, not in workflow code.
// POST .../values/{SHEETS_TAB_NAME}!A:J:append
// Body (values array, one row):
[
"{iso8601_timestamp}",
"{order_number}",
"{customer_email}",
"{carrier_name}",
"{tracking_number}",
"{tracking_url}",
"{klaviyo_status}", // 'sent', 'failed', 'suppressed'
"{ship_date}",
"{agent_run_id}",
"{notes}" // empty string unless error note applies
]
// Success: HTTP 200 with updatedRange in response bodySlack
Receives a daily digest summary from Agent 2 and posts it to the designated fulfilment channel. This is a non-blocking fire-and-forget action; failure here must not halt the core notification workflow.
Auth method
Slack Bot Token (xoxb-...) generated by installing a custom Slack app into the workspace. Stored in the credential store as SLACK_BOT_TOKEN. The bot must be invited to the target channel before it can post.
Required scopes
chat:write (post messages to channels the bot is a member of). No additional scopes are required for this use case. The app manifest should request only this scope to minimise permissions surface.
Webhook/trigger setup
Agent 2 calls POST https://slack.com/api/chat.postMessage with the bot token. Alternatively, an Incoming Webhook URL may be used (stored as SLACK_WEBHOOK_URL) for a simpler, scope-free implementation. The target channel ID is stored as SLACK_CHANNEL_ID in the credential store; never hardcode the channel name as it can be renamed.
Required configuration
SLACK_CHANNEL_ID and SLACK_BOT_TOKEN (or SLACK_WEBHOOK_URL) stored in credential store. The digest message must include: total notifications sent in the past 24 hours, count of failed sends, count of manual review flags raised, and a link to the Google Sheets log. Message formatting uses Slack Block Kit for readability.
Rate limits
Slack Web API: Tier 3 methods (chat.postMessage) allow 50 requests per minute. This automation posts at most once per day per batch run. No throttling is required.
Constraints
Slack delivery failure must be logged to Google Sheets in the Notes column but must not cause the workflow to halt or retry the Klaviyo send. The Slack step is always the final action in Agent 2 and is wrapped in a try-catch that suppresses the error after logging it.
// POST https://slack.com/api/chat.postMessage
// Headers: Authorization: Bearer {SLACK_BOT_TOKEN}
{
"channel": "{SLACK_CHANNEL_ID}",
"blocks": [
{ "type": "header", "text": { "type": "plain_text", "text": "Dispatch Summary" } },
{ "type": "section", "fields": [
{ "type": "mrkdwn", "text": "*Sent:* {sent_count}" },
{ "type": "mrkdwn", "text": "*Failed:* {failed_count}" },
{ "type": "mrkdwn", "text": "*Manual review:* {review_count}" },
{ "type": "mrkdwn", "text": "*Log:* <{sheets_url}|View Google Sheets>" }
]}
]
}
// Success: HTTP 200, ok: true in response bodyIntegration and API SpecPage 2 of 4
FS-DOC-05Technical
03Field mappings between tools
The tables below define every field passed between tools at each agent handoff. Use exact field names as shown; casing differences will cause silent mapping failures in most orchestration platforms.
Handoff A: ShipStation webhook payload to Agent 1 internal state (Shipment Event Listener)
Source tool
Source field
Destination tool
Destination field
ShipStation
resource_url
Agent 1 state
shipstation_resource_url
ShipStation (follow-up GET)
shipmentId
Agent 1 state
shipment_id
ShipStation (follow-up GET)
orderNumber
Agent 1 state
order_number_raw
ShipStation (follow-up GET)
trackingNumber
Agent 1 state
tracking_number
ShipStation (follow-up GET)
carrierCode
Agent 1 state
carrier_code
ShipStation (follow-up GET)
shipDate
Agent 1 state
ship_date
Handoff B: Agent 1 internal state to Shopify lookup and back (Shipment Event Listener)
Source tool
Source field
Destination tool
Destination field
Agent 1 state
order_number_raw
Shopify API query param
name
Shopify response
orders[0].name
Agent 1 state
order_number
Shopify response
orders[0].email
Agent 1 state
customer_email
Shopify response
orders[0].shipping_address.first_name
Agent 1 state
customer_first_name
Shopify response
orders[0].shipping_address.last_name
Agent 1 state
customer_last_name
Shopify response
orders[0].id
Agent 1 state
shopify_order_id
Handoff C: Agent 1 output object to Agent 2 (Shipment Event Listener to Notification Dispatch Agent)
Source tool
Source field
Destination tool
Destination field
Agent 1 output
order_number
Agent 2 input
order_number
Agent 1 output
customer_email
Agent 2 input
customer_email
Agent 1 output
customer_first_name
Agent 2 input
customer_first_name
Agent 1 output
tracking_number
Agent 2 input
tracking_number
Agent 1 output
carrier_code
Agent 2 input
carrier_code
Agent 1 output
tracking_url
Agent 2 input
tracking_url
Agent 1 output
ship_date
Agent 2 input
ship_date
Agent 1 output
shipment_id
Agent 2 input
agent_run_id
Handoff D: Agent 2 to Klaviyo Events API (Notification Dispatch Agent)
Source tool
Source field
Destination tool
Destination field
Agent 2 input
customer_email
Klaviyo event payload
data.attributes.profile.data.attributes.email
Agent 2 input
customer_first_name
Klaviyo event payload
data.attributes.properties.first_name
Agent 2 input
order_number
Klaviyo event payload
data.attributes.properties.order_number
Agent 2 input
tracking_url
Klaviyo event payload
data.attributes.properties.tracking_url
Agent 2 input
carrier_code
Klaviyo event payload (transformed)
data.attributes.properties.carrier_name
Agent 2 input
ship_date
Klaviyo event payload
data.attributes.properties.ship_date
Handoff E: Agent 2 to Google Sheets append (Notification Dispatch Agent)
Source tool
Source field
Destination tool
Destination field
Agent 2 runtime
iso8601_timestamp
Google Sheets row
Column A: Timestamp
Agent 2 input
order_number
Google Sheets row
Column B: Order Number
Agent 2 input
customer_email
Google Sheets row
Column C: Customer Email
Agent 2 input
carrier_code (transformed)
Google Sheets row
Column D: Carrier
Agent 2 input
tracking_number
Google Sheets row
Column E: Tracking Number
Agent 2 input
tracking_url
Google Sheets row
Column F: Tracking URL
Klaviyo API response
HTTP status / ok flag
Google Sheets row
Column G: Klaviyo Status
Agent 2 input
ship_date
Google Sheets row
Column H: Ship Date
Agent 2 input
agent_run_id
Google Sheets row
Column I: Agent Run ID
Agent 2 runtime
error_note or empty string
Google Sheets row
Column J: Notes
carrier_code to carrier_name transformation: the orchestration layer must maintain a lookup map stored in the credential store (CARRIER_NAME_MAP) converting ShipStation carrier codes (e.g. 'ups', 'usps', 'fedex', 'dhl_express') to display names (e.g. 'UPS', 'USPS', 'FedEx', 'DHL Express'). If a carrier code is not found in the map, the raw code is passed through and a warning is appended to the Google Sheets Notes column. A missing carrier in the tracking URL builder map must trigger an alert to the Slack channel immediately.
04Build stack and orchestration
Orchestration layout
Two discrete workflows: one per agent. Agent 1 (Shipment Event Listener) and Agent 2 (Notification Dispatch Agent) run in sequence. Agent 1 completes its data assembly and passes a structured JSON object to Agent 2 via an internal workflow trigger or queue. Both workflows share a single encrypted credential store; no credential is hardcoded in workflow logic.
Agent 1 trigger mechanism
Primary: inbound webhook. ShipStation posts a SHIP_NOTIFY event to the orchestration layer's webhook endpoint. The endpoint validates the X-ShipStation-Hmac-SHA256 signature using SHIPSTATION_WEBHOOK_SECRET before the workflow proceeds. Fallback: polling GET /shipments every 10 minutes filtered by shipDate=today and shipmentStatus=shipped. Polling mode is activated automatically if no webhook event is received within a configurable window (default: 15 minutes).
Agent 2 trigger mechanism
Internal trigger only. Agent 2 is invoked by Agent 1 upon successful assembly of the shipment data object. It does not expose a public webhook endpoint. The daily Slack digest is posted by a scheduled sub-step within Agent 2 that runs once per day at a configurable time (default: 18:00 local time), aggregating all run records from the Google Sheets log for that date.
Signature validation
ShipStation HMAC-SHA256 validation: compute HMAC of the raw request body using SHIPSTATION_WEBHOOK_SECRET and compare to the X-ShipStation-Hmac-SHA256 header value. Reject and log any request where the signatures do not match. Return HTTP 401 to the caller.
Credential store structure
All secrets stored as named encrypted variables in the orchestration platform's built-in credential store. See code block below for the full list. No credential appears in workflow code, environment config files, or any log output.
Carrier URL builder
A lookup table (CARRIER_TRACKING_URL_MAP) maps each ShipStation carrier code to its base tracking URL template. The tracking number is appended at the designated placeholder. The map is stored in the credential store as a JSON object, not in workflow code, so carrier additions require only a credential store update.
Error routing
Missing customer email: route to manual review branch (no email sent; log row written with status 'manual_review'). Missing carrier mapping: send Slack alert; attempt to pass raw tracking number with a placeholder URL; log with status 'carrier_unknown'. All unhandled exceptions: log to Google Sheets with status 'error'; post alert to Slack; do not fail silently under any circumstances.
All credential store entries must be populated before any workflow is activated. Never commit these values to source control or paste them into workflow node configuration fields.
// Credential store contents (all values encrypted at rest)
// ShipStation
SHIPSTATION_API_KEY = "<ShipStation API key>"
SHIPSTATION_API_SECRET = "<ShipStation API secret>"
SHIPSTATION_WEBHOOK_SECRET = "<HMAC signing secret from ShipStation webhook settings>"
SHIPSTATION_WEBHOOK_ID = "<registered webhook ID>"
// Shopify
SHOPIFY_ACCESS_TOKEN = "<Admin API access token>"
SHOPIFY_STORE_DOMAIN = "yourcompany.myshopify.com"
SHOPIFY_ORDER_PREFIX = "#" // update if custom prefix in use
// Klaviyo
KLAVIYO_PRIVATE_API_KEY = "<Klaviyo private API key>"
KLAVIYO_FLOW_ID = "<Klaviyo flow ID for Shipment Tracked trigger>"
KLAVIYO_LIST_ID = "<seed list ID for integration testing>"
// Google Sheets
GOOGLE_SERVICE_ACCOUNT_JSON = "<full JSON key file contents>"
SHEETS_SPREADSHEET_ID = "<Google Sheets document ID from URL>"
SHEETS_TAB_NAME = "Notification Log"
// Slack
SLACK_BOT_TOKEN = "xoxb-<bot token>"
SLACK_CHANNEL_ID = "<target channel ID, not channel name>"
SLACK_WEBHOOK_URL = "<optional: Incoming Webhook URL>"
// Carrier tracking URL map (JSON object)
CARRIER_TRACKING_URL_MAP = {
"ups": "https://www.ups.com/track?tracknum={tracking_number}",
"usps": "https://tools.usps.com/go/TrackConfirmAction?tLabels={tracking_number}",
"fedex": "https://www.fedex.com/fedextrack/?trknbr={tracking_number}",
"dhl_express": "https://www.dhl.com/en/express/tracking.html?AWB={tracking_number}&brand=DHL"
}
// Carrier display name map (JSON object)
CARRIER_NAME_MAP = {
"ups": "UPS",
"usps": "USPS",
"fedex": "FedEx",
"dhl_express": "DHL Express"
}Integration and API SpecPage 3 of 4
FS-DOC-05Technical
05Error handling and retry logic
Unhandled exceptions must never fail silently. Every integration point must have a defined failure path. If an error cannot be recovered automatically, it must be logged to Google Sheets and alerted to the Slack channel before the workflow exits. A silent failure in any node is a build defect.
Integration
Scenario
Required behaviour
ShipStation webhook
Webhook signature validation fails
Return HTTP 401. Log attempt with source IP and timestamp. Do not process payload. Trigger Slack alert if three invalid signatures are received within five minutes (potential misconfiguration or replay attack).
ShipStation webhook
Webhook delivery fails or is not received within 15 minutes
Activate polling fallback: GET /shipments filtered by today's shipDate. Run every 10 minutes. Suppress duplicate processing by checking shipment_id against Google Sheets Column I before proceeding. Log fallback activation to Google Sheets Notes column.
ShipStation API (follow-up GET)
HTTP 429 rate limit response
Pause workflow for 60 seconds and retry once. If a second 429 is received, pause for 120 seconds and retry a final time. After three failures, log to Google Sheets with status 'api_rate_limited' and post Slack alert. Do not drop the record.
ShipStation API (follow-up GET)
HTTP 5xx server error
Retry with exponential backoff: 30s, 90s, 270s (three attempts). After three failures, log shipment_id and order_number to Google Sheets with status 'shipstation_error' and alert Slack. Queue for manual retry.
Shopify API (order lookup)
Order not found for given order number
Log to Google Sheets with status 'order_not_found'. Route to manual review queue. Post Slack alert listing order_number and shipment_id. Do not proceed to Klaviyo step. Do not retry automatically; manual confirmation is required before resubmission.
Shopify API (order lookup)
Customer email field is null or fails format check
Route to manual review branch. Write log row with status 'missing_email'. Post Slack alert with order_number. No email is sent. The fulfilment coordinator must resolve the email address and resubmit the record manually.
Tracking URL builder
Carrier code not found in CARRIER_TRACKING_URL_MAP
Construct a fallback record with tracking_url set to empty string and carrier_name set to the raw carrier_code value. Log with status 'carrier_unknown'. Post immediate Slack alert to the fulfilment channel with the unknown carrier code. Do not send email to customer with a broken link. Route to manual review.
Klaviyo Events API
HTTP 400 bad request (malformed payload or invalid email)
Do not retry. Log to Google Sheets with status 'klaviyo_rejected' and include the full error body in the Notes column. Post Slack alert. The email address and payload must be inspected manually before resubmission.
Klaviyo Events API
HTTP 429 rate limit response
Retry with exponential backoff: 15s, 45s, 135s (three attempts). At current volume of ~15 events/day, this scenario is unlikely but must be handled. After three failures log with status 'klaviyo_rate_limited' and alert Slack.
Klaviyo Events API
HTTP 5xx server error
Retry with exponential backoff: 30s, 90s, 270s. After three failures, log with status 'klaviyo_server_error' and post Slack alert. The Google Sheets row is written regardless of Klaviyo outcome so the record is never lost.
Google Sheets API (append row)
HTTP 429 or write quota exceeded
Retry once after 5 seconds. If the second attempt also fails, store the row data in the orchestration platform's internal error log and post a Slack alert with the row contents so the record can be manually added. The Klaviyo send is not rolled back.
Slack (chat.postMessage)
HTTP error or channel not found
Log the failure to the orchestration platform's internal error log. Do not retry more than once. The Slack step is non-blocking; a Slack failure must never cause the core notification workflow to halt or revert. Suppress the error after logging.
For questions about this specification or to report a discrepancy between this document and the implemented build, contact the FullSpec team at support@gofullspec.com. Reference the process name 'Shipping and Tracking Notification' and the document code FS-DOC-05 in your subject line.
Integration and API SpecPage 4 of 4