Skip to Content
InternalDocsRunbooksCustomer Onboarding

Customer Onboarding

Source: docs/runbooks/customer-onboarding.md

# Customer Onboarding Runbook Purpose: onboard a new customer with what is live today (assisted onboarding), including tenant setup, integration keys, first upload validation, and manual billing ops. --- ## Reality Check (Live Today) | Capability | Current path | |---|---| | Create tenant | `POST /api/admin/tenants` (recommended) or `tenant:invite` CLI | | Issue integration key | `POST /api/admin/tenants/:id/integration-keys` (recommended) or `POST /api/integration-keys` (tenant context) | | Configure detectors | `POST /api/admin/tenants/:id/detectors` | | Onboarding status API | `GET /api/admin/tenants/:id/onboarding-status` | | Carrier account linking | Not yet exposed via `/api/admin/tenants/:id/carrier-accounts` | Notes: - This is an assisted onboarding flow. Public self-serve signup is out of scope. - Billing is internal invoice lifecycle. Stripe checkout/subscriptions are not implemented. --- ## Prerequisites - API deployed and healthy (`GET /health` returns `ok` or `degraded`) - Admin auth path available (Clerk admin session or admin-scoped bearer token) - `KEY_HASH_SECRET` configured in API runtime (required for integration-key auth) - Customer agreement signed and module scope confirmed (`ship`, `trade`, or both) --- ## Assisted Onboarding Flow (Recommended) ### 1. Create Tenant (Admin API) ```bash curl -X POST {{BASE_URL}}/api/admin/tenants \ -H "Authorization: Bearer <admin-jwt>" \ -H "Content-Type: application/json" \ -d '{ "name": "Acme Corp", "slug": "acme-corp", "modules": ["ship", "trade"], "clerkOrgId": "org_123", "primaryContact": { "email": "ops@acme.com", "role": "org:admin" } }' ``` Returns tenant metadata plus `inviteStatus`. Fallback (CLI): ```bash pnpm --filter @rgl8r/api tenant:invite -- \ --name "Acme Corp" \ --email "ops@acme.com" \ --role "org:admin" ``` ### 2. Create Integration Key (Admin API) ```bash curl -X POST {{BASE_URL}}/api/admin/tenants/{{TENANT_ID}}/integration-keys \ -H "Authorization: Bearer <admin-jwt>" \ -H "Content-Type: application/json" \ -d '{ "name": "Production CLI", "scopes": ["ship:upload", "ship:read", "jobs:read", "trade:upload", "trade:read"], "allowedAdapters": ["generic-shipment-csv", "catalog-excel"], "expiresAt": null }' ``` Important: - Secret is returned once. - Rotation endpoint: `POST /api/integration-keys/:id/rotate` (tenant-scoped route). ### 3. Configure Detector Overrides (Optional) ```bash curl -X POST {{BASE_URL}}/api/admin/tenants/{{TENANT_ID}}/detectors \ -H "Authorization: Bearer <admin-jwt>" \ -H "Content-Type: application/json" \ -d '{ "detectors": [ { "detectorCode": "AMOUNT_VARIANCE", "enabled": true }, { "detectorCode": "SIMA_EXPOSURE", "enabled": true } ] }' ``` If you skip this step, module defaults apply. ### 4. Verify Onboarding Status ```bash curl -H "Authorization: Bearer <admin-jwt>" \ "{{BASE_URL}}/api/admin/tenants/{{TENANT_ID}}/onboarding-status" ``` --- ## Customer Validation Flow ### 1. Exchange integration key for JWT ```bash curl -X POST {{BASE_URL}}/api/auth/token/integration \ -H "x-api-key: <integration-secret>" ``` Use returned `access_token` as `Authorization: Bearer <token>`. ### 2. Upload test data SHIP upload: ```bash curl -X POST {{BASE_URL}}/api/ship/upload \ -H "Authorization: Bearer <customer-jwt>" \ -F "file=@/path/to/test-shipments.csv" ``` TRADE upload: ```bash curl -X POST {{BASE_URL}}/api/upload \ -H "Authorization: Bearer <customer-jwt>" \ -F "file=@/path/to/test-data.xlsx" ``` Optional TRADE authority selection: - Add `screeningAuthority=US` form field for US AD/CVD screening. ### 3. Validate outputs ```bash curl -H "Authorization: Bearer <customer-jwt>" "{{BASE_URL}}/api/jobs/{{JOB_ID}}" curl -H "Authorization: Bearer <customer-jwt>" "{{BASE_URL}}/api/ship/findings?limit=20" curl -H "Authorization: Bearer <customer-jwt>" "{{BASE_URL}}/api/sima/results?limit=20" ``` --- ## Go-Live + Billing Ops 1. Customer starts production uploads. 2. Monitor first 24 hours for ingest failures and output quality. 3. Schedule one-week review. 4. Billing (manual invoice lifecycle): - Generate: `POST /api/admin/billing/invoices/generate` - Issue: `POST /api/admin/billing/invoices/:id/issue` - Mark paid: `POST /api/admin/billing/invoices/:id/mark-paid` - Cancel: `POST /api/admin/billing/invoices/:id/cancel` Billing note: - This is internal invoice workflow, not Stripe automation. - Payment collection and reconciliation are currently handled outside the API. --- ## Checklist ### Pre-Onboarding - [ ] Agreement signed - [ ] Module scope confirmed - [ ] Admin auth and key material verified (`KEY_HASH_SECRET` present) ### Setup - [ ] Tenant created - [ ] Integration key issued and stored by customer - [ ] Detector configuration confirmed (defaults or overrides) ### Validation - [ ] Token exchange succeeds (`/api/auth/token/integration`) - [ ] First upload completes - [ ] Findings/results reviewed with customer ### Go-Live - [ ] Production uploads started - [ ] 24-hour monitoring completed - [ ] 1-week review scheduled - [ ] Billing runbook executed if billing is active --- ## Troubleshooting | Problem | Likely cause | Fix | |---|---|---| | `401` on upload | JWT expired/invalid or integration key revoked | Re-exchange via `/api/auth/token/integration`; rotate key if needed | | `403` on upload | Missing required scopes | Reissue key with correct scopes | | `401` on `/api/auth/token/integration` with valid-looking key | Key revoked/expired, or `KEY_HASH_SECRET` missing/misconfigured (non-prod) | Verify key status; verify `KEY_HASH_SECRET`. In production, missing `KEY_HASH_SECRET` prevents API startup. | | Upload accepted but no findings | Clean data or detectors disabled | Verify sample data and detector config rows | | Billing generate fails `400` | Missing required body fields | Include `tenantId`, `periodStart`, `periodEnd` | --- ## Related Docs - [Assisted Public Launch Checklist](./assisted-public-launch-checklist.md) - [Build & Deploy](../BUILD_AND_DEPLOY.md) - [Integration Keys Contract](../integration-keys.md) - [Pilot Validation Runbook](../PILOT_VALIDATION_RUNBOOK.md)