Setup links
Customers
A Customer represents one of your platform’s end-tenants. It is the rightful owner of zero or more WhatsApp accounts you operate on its behalf.
cus_335T08RM0EAKN9DTE6RD5RWP7B| Resource type | customer |
| Public id prefix | cus_ |
| Scope | Team (which belongs to your Organization) |
| Soft-deleteable? | Yes (archive action; data preserved) |
| Mutation events | customer.created, customer.updated, customer.archived, customer.onboarded |
Fields
Section titled “Fields”| Field | Type | Description |
|---|---|---|
id | string | Public id, cus_… |
object | literal | Always 'customer' |
name | string | Display name (1-200 chars). Server-side sanitised — whitespace collapsed, no newlines. Surfaced in dashboard, onboarding page, audit logs. |
email | string | null | Optional contact email. Free-form, not validated for existence. |
status | enum | One of pending, active, suspended, archived. See status lifecycle below. |
metadata | object | null | Free-form JSON. Capped at 64 keys and 16 KB serialised. Use for your own foreign keys (CRM id, segment, locale, …). |
archived_at | string | null | ISO 8601. Set only when status = 'archived'. |
team_id | string | Internal team id this customer belongs to. Discoverable via GET /v1/me. |
created_at | string | ISO 8601. |
updated_at | string | ISO 8601. |
When you fetch a single customer via GET /v1/customers/{id}, the
response also includes:
| Field | Type | Description |
|---|---|---|
whatsapp_accounts | array | Trimmed projection of every account currently assigned to this customer. Each entry carries phone_number_id, phone_number, name, status, onboarded_at. |
Status lifecycle
Section titled “Status lifecycle”Four states. Most customers spend their whole life in active once
onboarded.
| Status | Meaning |
|---|---|
pending | Created but no WhatsApp account connected yet. The default for any new customer. |
active | At least one WhatsApp account is onboarded. Set automatically the moment a setup link is consumed. |
suspended | Operator-initiated freeze. Dashboard composer is blocked; Public API sends still flow (use account-level controls if you need to fully stop). |
archived | Soft-deleted. WhatsApp accounts and message history are preserved; only the customer row is hidden from default list views. |
Transitions
Section titled “Transitions”| From | To | How |
|---|---|---|
(new) | pending | POST /v1/customers |
pending | active | Automatic on customer.setup_link.consumed |
active | suspended | PATCH /v1/customers/{id} with status: 'suspended' |
suspended | active | PATCH /v1/customers/{id} with status: 'active' |
| any non-archived | archived | DELETE /v1/customers/{id} |
archived | pending | Dashboard “Restore” button (POST /api/customers/{id}/restore) |
Multi-team resolution
Section titled “Multi-team resolution”Customers live inside a specific Team. Most organisations have exactly
one Team, so the SDK and API can pick automatically. Multi-team orgs
must pass team_id explicitly:
// Single-team org — worksawait kirim.customers.create({ name: 'Acme Logistics' })
// Multi-team org — requiredawait kirim.customers.create({ team_id: 't1_v477nbrhghbi', name: 'Acme Logistics',})Behaviour matrix:
| Org has | team_id omitted | team_id provided |
|---|---|---|
| 1 team | Auto-selected | Validated; rejected if cross-org |
| 2+ teams | 400 missing_required_field, param: team_id | Validated; rejected if cross-org |
| 0 teams | 400 invalid_field_value | (same) |
Look up your team ids via GET /v1/me:
curl -sS https://api.kirimdev.com/v1/me \ -H 'Authorization: Bearer kdv_live_xxx'Relationship to WhatsApp accounts
Section titled “Relationship to WhatsApp accounts”A WhatsApp account has a nullable customer_id:
customer_idset → owned by a customer (multi-tenant). Surfaced in every response that embeds the account (conversation, contact, template, accounts list).customer_id = null→ direct-org account. Legacy behaviour, no customer attribution.
Filter accounts by customer:
curl -sS 'https://api.kirimdev.com/v1/accounts?customer_id=cus_335T08...' \ -H 'Authorization: Bearer kdv_live_xxx'A WhatsApp account can be assigned to at most one customer at a time. Reassignment is supported through dashboard “Assign / Unassign” actions; the Public API does not expose this yet.
Relationship to webhook events
Section titled “Relationship to webhook events”Six event types fire across a customer’s lifecycle. See Webhooks for the full payload shapes.
| Trigger | Event |
|---|---|
POST /v1/customers | customer.created |
PATCH /v1/customers/{id} | customer.updated |
DELETE /v1/customers/{id} | customer.archived |
| End-customer completes Embedded Signup via a setup link | customer.setup_link.consumed + customer.onboarded |
POST /v1/customers/{id}/setup_links | customer.setup_link.created |
Relationship to messages, conversations, contacts
Section titled “Relationship to messages, conversations, contacts”Once a customer’s WhatsApp account is connected, you address it the same way as any other account:
const phoneNumberId = '1111475158712095' // from the customer detail or webhook
const messages = await kirim .phoneNumbers(phoneNumberId) .messages.list() .page()There is no special code path for customer-owned accounts. The
embedded whatsapp_account object in every conversation, contact, and
template response carries the customer attribution so your application
can route by tenant:
"whatsapp_account": { "phone_number": "+62 857-2516-5424", "phone_number_id": "1111475158712095", "customer": { "id": "cus_335T08RM0EAKN9DTE6RD5RWP7B", "name": "Acme Logistics" }}When the account is direct-org, customer is null.
Limits
Section titled “Limits”| Limit | Value | Where enforced |
|---|---|---|
name length | 200 chars | Validation + sanitisation |
email length | 255 chars | Validation |
metadata keys | 64 max | Validation |
metadata serialised size | 16 KB max | Validation |
| Setup links per customer (list) | 50 most recent | Service |
| Setup link TTL | 1 hour to 30 days | Validation |
Errors
Section titled “Errors”Customer-related errors follow the standard /v1 envelope. Codes you
might see:
error.code | Status | When |
|---|---|---|
resource_not_found | 404 | Unknown cus_… id, or the customer belongs to a different org. |
invalid_field_value | 400 | Field length, URL shape, or enum violation. Check error.param. |
missing_required_field | 400 | Multi-team org called create without team_id. |
subscription_inactive | 402 | Org’s subscription expired; writes are blocked until upgrade. |
See Error catalogue for the full list.