Back to 3PL / Warehouse Coordination

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

3PL / Warehouse Coordination Automation

[YourCompany.com] · Fulfilment Department · Prepared by FullSpec · [Today's Date]

This document is the primary technical reference for the FullSpec team building the 3PL / Warehouse Coordination automation. It covers the current-state process map with bottleneck identification, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. All build decisions, credential requirements, and implementation constraints the FullSpec team must resolve before or during the build sprint are called out explicitly in each section.

01Process snapshot

Step
Name
Description
1
Log Into 3PL Portal and Pull Report
Operations Coordinator logs into ShipBob each morning and downloads the daily inventory and shipment status report as CSV or Excel. Time cost: 20 min/day.
2
Reformat and Clean the Downloaded Data
Raw export is reformatted in Google Sheets to match internal OMS column structure, removing duplicates and correcting SKU codes that differ between systems. BOTTLENECK. Time cost: 30 min/day.
3
Compare 3PL Stock Counts to OMS Inventory
Coordinator runs a manual VLOOKUP comparison between 3PL stock levels and OMS inventory records, highlighting lines where counts differ beyond a set threshold. BOTTLENECK. Time cost: 35 min/day.
4
Chase Missing or Unconfirmed ASNs
Inbound purchase orders with no confirmed ASN from the 3PL are identified and the coordinator sends follow-up email to the 3PL contact. Time cost: 25 min/day.
5
Investigate and Resolve Discrepancies
Flagged discrepancies are investigated by cross-referencing shipment manifests and carrier tracking numbers, then emailing or calling the 3PL to confirm the correct count. BOTTLENECK. Time cost: 40 min/day (can extend to several hours).
6
Update OMS Inventory Records Manually
Once discrepancies are resolved, the coordinator manually adjusts stock levels in Cin7 to match confirmed 3PL counts, adding a note for each adjustment. Time cost: 25 min/day.
7
Update Shopify Product Stock Levels
Corrected stock levels are manually entered or imported into Shopify to prevent overselling, requiring a separate login and per-variant update. Time cost: 20 min/day.
8
Notify Team of Delays or Exceptions
Delayed shipments, short picks, or unresolved exceptions are summarised and messaged to the operations team via Slack or email. Time cost: 15 min/day.
9
Log Adjustments and File Report
Coordinator updates the weekly reconciliation log in Google Sheets with all changes made, flags for finance, and saves the original 3PL export for audit purposes. Time cost: 20 min/day.
10
Share Reconciliation Summary with Finance
A summary of inventory adjustments is emailed to the finance team so that stock valuations in Xero can be reviewed and updated as needed. Time cost: 10 min/day.
Time cost summary: Total manual time per cycle is 240 minutes (4 hours) per reconciliation run. At one run per business day this equates to approximately 8 hours per week across the team, costing $16,640/year at the assumed coordinator rate. The automation replaces steps 1, 2, 3, 5, 6, 7, 8, 9, and 10. Step 4 (chasing missing ASNs) is partially replaced: the automation detects and flags missing ASNs automatically via Slack alert, but direct 3PL follow-up communication remains a human task. Step 5 (investigate and resolve major discrepancies) is retained as a human step for all variances above the configured threshold.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

3PL Data Fetch and Normalisation Agent

This agent is the entry point of the automation. It connects to the ShipBob API on both a daily schedule and an event-driven webhook trigger, pulls current inventory levels, inbound receipt statuses, and outbound dispatch records, then normalises all SKU references against the internal SKU mapping table before passing a clean, structured dataset downstream to the Reconciliation and Discrepancy Agent. It does not write to any system; its sole responsibility is clean data delivery. Estimated build time: 10 hours. Complexity: Moderate.

Trigger
Daily schedule (configurable time, default 06:00 local) OR ShipBob inbound/outbound webhook received (event: inventory_updated, receiving_order_created, outbound_order_fulfilled).
Tools
ShipBob (REST API v2, OAuth 2.0 bearer token)
Replaces steps
Steps 1 and 2 (portal login, report download, and data reformatting)
Estimated build
10 hours, Moderate complexity
// Input
trigger_type: 'schedule' | 'webhook'
webhook_event?: 'inventory_updated' | 'receiving_order_created' | 'outbound_order_fulfilled'
webhook_payload?: ShipBobWebhookPayload
schedule_date: ISO8601 date string

// ShipBob API calls made by this agent
GET /v2/inventory          -> raw_inventory_lines[]
GET /v2/receiving-orders   -> raw_asn_lines[]
GET /v2/orders?status=Fulfilled -> raw_outbound_lines[]

// SKU normalisation (mapping table lookup)
shipbob_sku        -> internal_sku   (via Google Sheets SKU mapping table)
shipbob_location   -> warehouse_code
shipbob_quantity   -> quantity_on_hand (integer)
shipbob_asn_status -> asn_status: 'confirmed' | 'pending' | 'missing'

// Output
normalised_dataset: {
  run_id: UUID,
  run_timestamp: ISO8601,
  trigger_type: string,
  inventory_lines: [
    {
      internal_sku: string,
      shipbob_sku: string,
      warehouse_code: string,
      quantity_on_hand: integer,
      asn_status: string,
      asn_id?: string,
      last_updated: ISO8601
    }
  ],
  unmapped_skus: string[]   // SKUs with no mapping table entry
}
  • ShipBob API tier: ShipBob's REST API v2 is available on all paid merchant plans. Confirm the account has API access enabled and that an OAuth 2.0 application has been registered in the ShipBob merchant portal before build begins. The access token must be stored in the shared credential store, not hard-coded.
  • Webhook registration: ShipBob webhooks must be registered via POST /v2/webhook with the automation platform's publicly accessible endpoint URL. Register all three event types: inventory_updated, receiving_order_created, outbound_order_fulfilled. Confirm the endpoint is HTTPS only.
  • SKU mapping table: The normalisation step depends entirely on a maintained Google Sheets SKU mapping table (columns: shipbob_sku, internal_sku, warehouse_code, active: boolean). Priya Lal is compiling this table (milestone: May 20). Build cannot proceed to normalisation logic until the table schema is finalised and at least one full SKU set is populated.
  • Unmapped SKU fallback: Any ShipBob SKU not found in the mapping table must be collected into the unmapped_skus array and surfaced in the daily Slack summary as a separate warning, not silently dropped. This prevents false-negative reconciliation results.
  • Dedupe on schedule + webhook: If a webhook fires on the same day as the scheduled run, the agent must check run_id uniqueness against the reconciliation log before passing downstream, to avoid double-processing the same inventory snapshot.
  • Rate limits: ShipBob API enforces 100 requests per minute per merchant token. Paginate all list endpoints with a page_size of 50 and include a 700 ms inter-request delay when fetching large inventories.
Reconciliation and Discrepancy Agent

This agent receives the normalised dataset from the Data Fetch agent and performs the core reconciliation logic. It calls the Cin7 API to retrieve live inventory counts for each internal SKU, compares them line by line against the ShipBob-sourced quantities, and classifies each SKU as matched, minor variance, or major discrepancy based on configured threshold values agreed with the operations manager. It also flags any SKU with an asn_status of missing or pending beyond the agreed age threshold. Results are written as a new row to the Google Sheets reconciliation log, and the classified output is passed to the Update and Notification Agent. Estimated build time: 16 hours. Complexity: Complex.

Trigger
Normalised dataset received from 3PL Data Fetch and Normalisation Agent (run_id, inventory_lines[], unmapped_skus[]).
Tools
Cin7 (REST API, API key auth); Google Sheets (Sheets API v4, service account OAuth)
Replaces steps
Steps 3, 5, 9, and 10 (comparison, discrepancy investigation routing, log filing, and finance summary row)
Estimated build
16 hours, Complex complexity
// Input
run_id: UUID
run_timestamp: ISO8601
inventory_lines: [
  { internal_sku, shipbob_sku, warehouse_code, quantity_on_hand, asn_status, asn_id?, last_updated }
]
unmapped_skus: string[]

// Cin7 API call per SKU
GET /v1/inventory?sku={internal_sku}  ->  cin7_quantity_on_hand: integer
                                      ->  cin7_product_id: string
                                      ->  cin7_last_modified: ISO8601

// Classification logic
variance = shipbob_quantity_on_hand - cin7_quantity_on_hand
variance_pct = abs(variance) / cin7_quantity_on_hand
classification:
  'matched'           if variance_pct <= minor_threshold (default 2%)
  'minor_variance'    if minor_threshold < variance_pct <= major_threshold (default 10%)
  'major_discrepancy' if variance_pct > major_threshold OR abs(variance) > major_unit_threshold (default 10 units)
  'missing_asn'       if asn_status == 'missing' OR (asn_status == 'pending' AND age > asn_age_threshold_hours, default 24)

// Output (passed to Update and Notification Agent)
reconciliation_result: {
  run_id: UUID,
  run_timestamp: ISO8601,
  classified_lines: [
    {
      internal_sku: string,
      shipbob_sku: string,
      cin7_product_id: string,
      warehouse_code: string,
      shipbob_qty: integer,
      cin7_qty: integer,
      variance: integer,
      variance_pct: float,
      classification: 'matched' | 'minor_variance' | 'major_discrepancy' | 'missing_asn',
      asn_id?: string,
      asn_status: string
    }
  ],
  unmapped_skus: string[],
  total_skus_processed: integer,
  matched_count: integer,
  minor_variance_count: integer,
  major_discrepancy_count: integer,
  missing_asn_count: integer
}

// Google Sheets append (reconciliation log)
Sheet: 'Reconciliation Log'
Row fields: run_id, run_timestamp, total_skus_processed, matched_count,
            minor_variance_count, major_discrepancy_count, missing_asn_count,
            unmapped_skus (comma-separated), status: 'complete' | 'has_exceptions'
  • Cin7 API auth: Cin7 Core uses a per-account API key passed as a header (Authorization: Basic base64(account_id:api_key)). Confirm the account tier supports API access. Cin7 Omni uses a different OAuth flow. Clarify which Cin7 variant the client uses before building the integration module.
  • Threshold configuration: The values for minor_threshold (percentage), major_threshold (percentage), major_unit_threshold (units), and asn_age_threshold_hours must be confirmed with the operations manager before build. Store these as environment variables in the automation platform credential store so they can be adjusted without a rebuild.
  • Cin7 pagination: The Cin7 inventory endpoint returns a maximum of 250 records per page. If the SKU count exceeds 250, implement paginated fetching with the rows and page parameters.
  • Google Sheets service account: The reconciliation log Google Sheet must be shared with the service account email generated during OAuth setup. The sheet ID and tab name ('Reconciliation Log') must be stored as environment variables, not hard-coded.
  • Missing ASN detection: A SKU classified as missing_asn must not be auto-corrected under any threshold. It must always route to the major discrepancy branch regardless of unit variance.
  • Step 10 replacement: The finance summary row appended to Google Sheets replaces the manual email to the finance team (Step 10). A separate tab ('Finance Summary') should be maintained with one aggregated row per run for Xero cross-reference. Confirm with Sam Okafor whether a Slack DM to the finance channel is also required on top of the Sheets row.
Update and Notification Agent

This agent acts on the classified reconciliation results. For all SKUs classified as matched or minor variance, it applies the confirmed stock quantity directly to Cin7 via the inventory adjustment endpoint and then pushes the updated level to Shopify per product variant. For all SKUs classified as major discrepancy or missing ASN, it posts a structured alert to the designated Slack fulfilment channel instead of auto-correcting. At the end of each run it assembles and posts a daily summary message to the Slack fulfilment channel covering all SKUs processed, variances found, adjustments made, and open exceptions. Estimated build time: 12 hours. Complexity: Moderate.

Trigger
Classified reconciliation_result object received from Reconciliation and Discrepancy Agent.
Tools
Cin7 (REST API, inventory adjustment endpoint); Shopify (Admin REST API v2024-01, inventory_levels/set.json); Slack (Web API, chat.postMessage, Bot token)
Replaces steps
Steps 6, 7, and 8 (Cin7 update, Shopify update, team Slack notification)
Estimated build
12 hours, Moderate complexity
// Input
reconciliation_result: { ... }  // full object from Reconciliation Agent

// Branch A: auto-update (matched and minor_variance SKUs)
POST /v1/inventory/adjustments  (Cin7)
  body: { productId: cin7_product_id, quantity: shipbob_qty, note: 'Auto-reconciled by automation run_id {run_id} at {run_timestamp}' }

GET /admin/api/2024-01/variants.json?sku={internal_sku}  (Shopify)
  -> variant_id, inventory_item_id, location_id
POST /admin/api/2024-01/inventory_levels/set.json  (Shopify)
  body: { location_id, inventory_item_id, available: shipbob_qty }

// Branch B: exception alert (major_discrepancy and missing_asn SKUs)
POST https://slack.com/api/chat.postMessage
  channel: '#fulfilment-ops'
  blocks: [
    { type: 'header', text: ':rotating_light: Stock Discrepancy Alert' },
    { type: 'section', fields: [
      'SKU: {internal_sku}',
      'ShipBob qty: {shipbob_qty}',
      'Cin7 qty: {cin7_qty}',
      'Variance: {variance} units ({variance_pct}%)',
      'Classification: {classification}',
      'ASN ID: {asn_id | none}',
      'Cin7 record: https://app.cin7.com/inventory/{cin7_product_id}'
    ]}
  ]

// Daily summary message (posted after all lines processed)
POST https://slack.com/api/chat.postMessage
  channel: '#fulfilment-ops'
  text: 'Daily 3PL Reconciliation Summary — {run_timestamp}'
  blocks: [
    'Total SKUs processed: {total_skus_processed}',
    'Matched and auto-updated: {matched_count + minor_variance_count}',
    'Major discrepancies (action required): {major_discrepancy_count}',
    'Missing ASNs (action required): {missing_asn_count}',
    'Unmapped SKUs (mapping table update required): {unmapped_skus}'
  ]

// Output
update_summary: {
  run_id: UUID,
  cin7_adjustments_applied: integer,
  shopify_variants_updated: integer,
  slack_alerts_sent: integer,
  daily_summary_sent: boolean,
  errors: [{ sku: string, system: string, error_message: string }]
}
  • Shopify native sync check: If Cin7 and Shopify are already connected via Cin7's native Shopify integration, pushing stock updates directly to Shopify via the Admin API from this agent will cause double-adjustment. This must be confirmed during scoping. If a native sync is active, the Shopify update branch of this agent should be disabled and Cin7 should be treated as the single source of truth, with Shopify syncing from it automatically.
  • Shopify location ID: Shopify inventory is location-scoped. The relevant Shopify location_id (corresponding to the ShipBob warehouse) must be retrieved and stored as an environment variable before build. Use GET /admin/api/2024-01/locations.json to enumerate available locations during setup.
  • Slack bot token: A Slack app with the chat:write and channels:read OAuth scopes must be created in the [YourCompany.com] Slack workspace. The bot must be invited to #fulfilment-ops before the integration can post. Store the bot token (xoxb-...) in the shared credential store.
  • Cin7 adjustment note format: Each Cin7 inventory adjustment must include a structured note in the format 'Auto-reconciled by automation run_id {run_id} at {run_timestamp}' so manual audits can distinguish automated adjustments from human edits.
  • Error handling: Any failed Cin7 or Shopify API call must be retried once after a 5-second delay. If the retry also fails, the SKU must be added to the errors array, logged to the error table in Supabase, and included in the daily Slack summary as a separate 'Failed updates' line rather than silently skipped.
  • Slack rate limit: The Slack Web API enforces a rate limit of approximately 1 message per second on chat.postMessage. If multiple discrepancy alerts are generated in a single run, introduce a 1.1-second delay between each alert post to avoid 429 errors. Bundle alerts into a single multi-section Block Kit message if more than 5 exceptions exist in one run.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow — 3PL / Warehouse Coordination Automation
// ============================================================
// TRIGGER
// ============================================================
TRIGGER: daily_schedule (06:00 local) OR shipbob_webhook_received
  -> trigger_type: 'schedule' | 'webhook'
  -> webhook_event?: 'inventory_updated' | 'receiving_order_created' | 'outbound_order_fulfilled'
  -> schedule_date: ISO8601
  -> run_id: UUID (generated at trigger)

// ============================================================
// AGENT 1: 3PL Data Fetch and Normalisation Agent
// ============================================================
ShipBob API calls:
  GET /v2/inventory          -> raw_inventory_lines[]
  GET /v2/receiving-orders   -> raw_asn_lines[]
  GET /v2/orders?status=Fulfilled -> raw_outbound_lines[]

SKU mapping table lookup (Google Sheets):
  shipbob_sku       -> internal_sku
  unmapped shipbob_skus -> unmapped_skus[]  (surfaced in summary, not dropped)

Fields normalised per inventory line:
  shipbob_sku, internal_sku, warehouse_code,
  quantity_on_hand (integer), asn_status ('confirmed'|'pending'|'missing'),
  asn_id (string|null), last_updated (ISO8601)

Dedupe check: if run_id already exists in reconciliation log -> abort, log warning

// HANDOFF: normalised_dataset -> Reconciliation and Discrepancy Agent
// Payload: { run_id, run_timestamp, trigger_type, inventory_lines[], unmapped_skus[] }

// ============================================================
// AGENT 2: Reconciliation and Discrepancy Agent
// ============================================================
For each inventory_line in normalised_dataset.inventory_lines:
  Cin7 API call:
    GET /v1/inventory?sku={internal_sku}
      -> cin7_quantity_on_hand (integer)
      -> cin7_product_id (string)
      -> cin7_last_modified (ISO8601)

  Compute:
    variance = quantity_on_hand - cin7_quantity_on_hand
    variance_pct = abs(variance) / cin7_quantity_on_hand

  Classify:
    variance_pct <= 0.02                                  -> 'matched'
    0.02 < variance_pct <= 0.10 AND abs(variance) <= 10   -> 'minor_variance'
    variance_pct > 0.10 OR abs(variance) > 10             -> 'major_discrepancy'
    asn_status == 'missing' OR pending age > 24h          -> 'missing_asn' (overrides all)

Google Sheets append (Reconciliation Log tab):
  Fields: run_id, run_timestamp, total_skus_processed, matched_count,
          minor_variance_count, major_discrepancy_count, missing_asn_count,
          unmapped_skus (comma-separated), status

Google Sheets append (Finance Summary tab):
  Fields: run_id, run_timestamp, total_adjustments_value, open_exceptions_count

// HANDOFF: reconciliation_result -> Update and Notification Agent
// Payload: { run_id, run_timestamp, classified_lines[], unmapped_skus[],
//           total_skus_processed, matched_count, minor_variance_count,
//           major_discrepancy_count, missing_asn_count }

// ============================================================
// AGENT 3: Update and Notification Agent
// ============================================================
Branch A: classification IN ('matched', 'minor_variance')
  Cin7 inventory adjustment:
    POST /v1/inventory/adjustments
    body: { productId: cin7_product_id, quantity: shipbob_qty,
            note: 'Auto-reconciled by automation run_id {run_id} at {run_timestamp}' }
    -> cin7_adjustment_id (string)

  Shopify variant lookup (if native sync not active):
    GET /admin/api/2024-01/variants.json?sku={internal_sku}
    -> variant_id, inventory_item_id, location_id

  Shopify inventory level update:
    POST /admin/api/2024-01/inventory_levels/set.json
    body: { location_id, inventory_item_id, available: shipbob_qty }
    -> updated: boolean

Branch B: classification IN ('major_discrepancy', 'missing_asn')
  Slack alert per exception SKU:
    POST chat.postMessage -> channel: '#fulfilment-ops'
    fields: internal_sku, shipbob_qty, cin7_qty, variance,
            variance_pct, classification, asn_id, cin7_product_id (link)

End of run (all branches complete):
  Slack daily summary:
    POST chat.postMessage -> channel: '#fulfilment-ops'
    fields: run_timestamp, total_skus_processed, auto_updated_count,
            major_discrepancy_count, missing_asn_count, unmapped_skus,
            failed_updates (if any)

Error events (any API failure after 1 retry):
  -> Supabase error log table: run_id, sku, system, error_message, timestamp
  -> Included in daily Slack summary 'Failed updates' field

// ============================================================
// TERMINAL STATE
// ============================================================
Reconciliation log row: written (Google Sheets)
Finance summary row: written (Google Sheets)
Cin7 stock levels: updated for matched + minor_variance SKUs
Shopify inventory: updated for same SKUs (if native sync inactive)
Slack alerts: posted for each major_discrepancy and missing_asn SKU
Slack daily summary: posted to #fulfilment-ops
Error log: written to Supabase for any failed API calls
Human action required: Operations Coordinator reviews Slack alerts
                       for major discrepancies and missing ASNs
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform (build tool to be confirmed by the FullSpec team at sprint start). Structure: one workflow per agent, three workflows total. Each workflow is self-contained with its own error handler. Workflows communicate via an internal message queue or direct workflow-to-workflow trigger depending on the platform's native capability. All credentials (ShipBob OAuth token, Cin7 API key, Shopify Admin API key, Slack bot token, Google Sheets service account JSON) are stored in a shared credential store, not embedded in workflow nodes. Environment variables for threshold values (minor_threshold, major_threshold, major_unit_threshold, asn_age_threshold_hours, shopify_location_id, sheets_reconciliation_log_id, sheets_finance_summary_id, slack_channel_id) are stored separately from credentials and loaded at runtime.
Webhook configuration
ShipBob webhooks: registered via POST /v2/webhook during the Discovery and API Access Setup stage (Week 1). Three event subscriptions: inventory_updated, receiving_order_created, outbound_order_fulfilled. The automation platform must expose a publicly accessible HTTPS endpoint URL for each event type (or a single endpoint with event type routing via the X-ShipBob-Event header). Webhook payloads must be validated against ShipBob's HMAC signature (X-ShipBob-Hmac-SHA256 header) before the workflow proceeds. Invalid signatures must be rejected with HTTP 401 and logged.
Templating approach
Slack Block Kit JSON is constructed inline within the Update and Notification Agent workflow using template nodes or code nodes that assemble the block structure from classified_line fields. Cin7 adjustment note text uses a string interpolation template: 'Auto-reconciled by automation run_id {run_id} at {run_timestamp}'. Google Sheets row values are mapped field-by-field in the append node, with column order matching the sheet header row defined during setup. No external templating engine is required.
Error logging
A Supabase table named 'automation_errors' captures all API failures after retry exhaustion. Schema: id (uuid, pk), run_id (uuid), agent_name (text), sku (text), target_system (text), error_code (integer), error_message (text), created_at (timestamptz). A Supabase database webhook or a scheduled query (every 15 minutes during business hours) checks for new error rows and posts a consolidated alert to a separate Slack channel '#automation-alerts' so the FullSpec team is notified of infrastructure issues independently of the operations team's #fulfilment-ops channel.
Testing approach
All three agents are built and tested against sandbox or staging credentials first. ShipBob provides a sandbox environment for API testing. Cin7 staging can be used if the client has a test account; otherwise a mock API response fixture is used for unit testing of classification logic. Shopify sandbox testing uses the development store associated with the merchant account. Google Sheets testing uses a dedicated 'Reconciliation Log - TEST' sheet. Slack testing uses a private '#automation-test' channel. Parallel run (Week 5) compares automated reconciliation output against the manual process output for a minimum of five business days before go-live sign-off.
Estimated total build time
Agent 1 (3PL Data Fetch and Normalisation): 10 hours. Agent 2 (Reconciliation and Discrepancy): 16 hours. Agent 3 (Update and Notification): 12 hours. End-to-end integration testing and parallel run validation: 6 hours. Total: 44 hours. This matches the confirmed build_effort_hours figure from the project snapshot.
Pre-build blockers: Three items must be resolved before the build sprint begins on May 27. First, ShipBob, Cin7, and Shopify API credentials must be confirmed and accessible in the shared credential store (milestone: May 14, marked done). Second, the SKU mapping table must be finalised and shared with the FullSpec team in the agreed Google Sheets format (milestone: May 20, in progress with Priya Lal). Third, the operations manager must confirm the discrepancy threshold values (minor percentage, major percentage, major unit count, and ASN age limit) so they can be loaded as environment variables before the Reconciliation Agent build begins. Contact support@gofullspec.com if any of these items are blocked.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for 3PL / Warehouse Coordination.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View