Skip to content
Concepts

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 typecustomer
Public id prefixcus_
ScopeTeam (which belongs to your Organization)
Soft-deleteable?Yes (archive action; data preserved)
Mutation eventscustomer.created, customer.updated, customer.archived, customer.onboarded
FieldTypeDescription
idstringPublic id, cus_…
objectliteralAlways 'customer'
namestringDisplay name (1-200 chars). Server-side sanitised — whitespace collapsed, no newlines. Surfaced in dashboard, onboarding page, audit logs.
emailstring | nullOptional contact email. Free-form, not validated for existence.
statusenumOne of pending, active, suspended, archived. See status lifecycle below.
metadataobject | nullFree-form JSON. Capped at 64 keys and 16 KB serialised. Use for your own foreign keys (CRM id, segment, locale, …).
archived_atstring | nullISO 8601. Set only when status = 'archived'.
team_idstringInternal team id this customer belongs to. Discoverable via GET /v1/me.
created_atstringISO 8601.
updated_atstringISO 8601.

When you fetch a single customer via GET /v1/customers/{id}, the response also includes:

FieldTypeDescription
whatsapp_accountsarrayTrimmed projection of every account currently assigned to this customer. Each entry carries phone_number_id, phone_number, name, status, onboarded_at.

Four states. Most customers spend their whole life in active once onboarded.

StatusMeaning
pendingCreated but no WhatsApp account connected yet. The default for any new customer.
activeAt least one WhatsApp account is onboarded. Set automatically the moment a setup link is consumed.
suspendedOperator-initiated freeze. Dashboard composer is blocked; Public API sends still flow (use account-level controls if you need to fully stop).
archivedSoft-deleted. WhatsApp accounts and message history are preserved; only the customer row is hidden from default list views.
FromToHow
(new)pendingPOST /v1/customers
pendingactiveAutomatic on customer.setup_link.consumed
activesuspendedPATCH /v1/customers/{id} with status: 'suspended'
suspendedactivePATCH /v1/customers/{id} with status: 'active'
any non-archivedarchivedDELETE /v1/customers/{id}
archivedpendingDashboard “Restore” button (POST /api/customers/{id}/restore)

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 — works
await kirim.customers.create({ name: 'Acme Logistics' })
// Multi-team org — required
await kirim.customers.create({
team_id: 't1_v477nbrhghbi',
name: 'Acme Logistics',
})

Behaviour matrix:

Org hasteam_id omittedteam_id provided
1 teamAuto-selectedValidated; rejected if cross-org
2+ teams400 missing_required_field, param: team_idValidated; rejected if cross-org
0 teams400 invalid_field_value(same)

Look up your team ids via GET /v1/me:

Terminal window
curl -sS https://api.kirimdev.com/v1/me \
-H 'Authorization: Bearer kdv_live_xxx'

A WhatsApp account has a nullable customer_id:

  • customer_id set → 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:

Terminal window
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.

Six event types fire across a customer’s lifecycle. See Webhooks for the full payload shapes.

TriggerEvent
POST /v1/customerscustomer.created
PATCH /v1/customers/{id}customer.updated
DELETE /v1/customers/{id}customer.archived
End-customer completes Embedded Signup via a setup linkcustomer.setup_link.consumed + customer.onboarded
POST /v1/customers/{id}/setup_linkscustomer.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.

LimitValueWhere enforced
name length200 charsValidation + sanitisation
email length255 charsValidation
metadata keys64 maxValidation
metadata serialised size16 KB maxValidation
Setup links per customer (list)50 most recentService
Setup link TTL1 hour to 30 daysValidation

Customer-related errors follow the standard /v1 envelope. Codes you might see:

error.codeStatusWhen
resource_not_found404Unknown cus_… id, or the customer belongs to a different org.
invalid_field_value400Field length, URL shape, or enum violation. Check error.param.
missing_required_field400Multi-team org called create without team_id.
subscription_inactive402Org’s subscription expired; writes are blocked until upgrade.

See Error catalogue for the full list.