Manage customers
Create a customer up front, update, list and block one, keep its tax ids, and follow its webhooks.
Create a customer up front
Creating explicitly is worth it when you know the payer before they pay: a signup, an imported customer base, a B2B account with a tax id and a billing address.
https://api.wajub.com/customersAt least one of email or phone is required. Everything else is optional, and the fields below
are the ones worth knowing; the full list is in the API reference.
emailstringoptionalphone is given. Stored as sent, and found whatever its case: a second POST /customers with Amina@Example.com returns the customer stored as amina@example.com.phonestringoptionalemail is given. Stored in E.164: a number without its international prefix is read in phone_country, else your account's country.namestringoptionaltypeenumoptionaldefault : individualindividual or business. Sending business_name alone infers business.business_namestringoptionaltype is business. Becomes the customer name.currencystringoptionalpreferred_languagestringoptionaladdressobjectoptionalcountry, city, state, postal_code, address_line1, address_line2.billingobjectoptionaladdress.customer_groupstringoptionaltagsarrayoptionalmetadataobjectoptionalSend it from your server with the secret key.
curl https://api.wajub.com/customers \
-H "Authorization: sk_test.xxxxxxxxxxxxxxxx…" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: cus-signup-9182" \
-d '{
"email": "amina@example.com",
"phone": "+237670000000",
"name": "Amina Diallo",
"metadata": { "user_id": "u_9182" }
}'A new customer comes back as 201 Created, with the id you will pass on payments from now on.
A duplicate answers 200, not 201
Create a customer whose email (whatever its case) or phone already exists and the API returns the
existing record with 200 OK and the message Customer already exists for this merchant.
Nothing is duplicated, and nothing is updated either. Branch on code === 201 to tell a real
creation from a match, and never treat the 200 as an error.
Idempotency-Key works here as it does on payments: the same key replays the original response for
24 hours instead of opening a second record. It is the safe way to retry a signup hook.
Update a customer
A payment never corrects a customer, it only fills the fields still empty: see A payment never
overwrites a customer. Corrections go
through PUT /customers/{id}.
https://api.wajub.com/customers/{id}PUT /customers/{id} changes only the fields you send. The name changes only when you send it:
first_name and last_name are refused, and sending business_name on an individual leaves their
name alone (on a business, a new business_name becomes its name unless you send name too). An
email (whatever its case) or a phone already held by another customer of the account answers
422 on that field, A customer with this email already exists. or
A customer with this phone already exists.
Retrieve and list
Retrieving takes the customer id and returns the same object as the create call.
https://api.wajub.com/customers/{id}Listing returns your customers newest first, and accepts filters that map onto how you segment them.
https://api.wajub.com/customers| Query parameter | Accepts |
|---|---|
per_page | 1 to 100, defaults to 25 |
search | Free text over name, email, phone and id |
status | active, inactive, blocked |
type | individual, business |
country | Two-letter country code of the customer's shipping or billing address |
customer_group | The group string you set on the customer |
cursor | An opaque cursor from a previous response |
Pagination has two modes and the default is the page-number one. Send no cursor and meta holds
current_page, last_page, per_page and total, plus the next_cursor and has_more that let
you go on with a cursor. Send that next_cursor as cursor and the endpoint switches to cursor
mode, where meta holds per_page, next_cursor, prev_cursor and has_more instead.
Cursor mode is what you want for a full export, since it stays correct while new customers are
being created. Pagination covers both.
On behalf of a subaccount
A marketplace creating a customer on behalf of one of its subaccounts adds the X-Sync header to
POST /customers, and the duplicate rule above applies: when the merchant already has a customer
with this email or phone, that customer is returned with 200, and nothing is duplicated. The
platform's call only fills the fields the merchant left empty; it never changes one they set. The
customer returned, found or created, is open to the connection from then on: the platform can read,
list and update them, and use them on its payments and invoices. A payment created with X-Sync and
the email or phone of an existing customer is attached to that customer the same way.
With X-Sync, a customer never shows the merchant's notes, block_reason or metadata, and
notes sent with X-Sync is ignored. metadata is the platform's own: it reads and writes it as
on any customer, each connection has its own, and the merchant's stays theirs. On a customer the
merchant already had, POST /customers and payments add the keys the platform has not set yet,
and PUT /customers/{id} replaces the platform's metadata. Account
types has the details.
Status is a label, not a gate
A customer is active, inactive or blocked, and four endpoints move them between those states.
| Call | Effect |
|---|---|
POST /customers/{id}/block | Sets blocked, stores your reason and a blocked_at timestamp |
POST /customers/{id}/unblock | Returns the customer to active |
POST /customers/{id}/deactivate | Sets inactive |
POST /customers/{id}/activate | Sets active |
Blocking an already blocked customer answers 400, as does unblocking one who is not blocked.
Blocking a customer does not stop them paying
status: blocked is a flag on the customer record and nothing in the payment path reads it. A
blocked customer can create and complete a payment exactly as before. To actually refuse a payer,
add their email or phone to the Shield blocklist, which is checked on every live
transaction and rejects it outright.
Tax ids
A business customer's tax registration numbers are kept in their own list, one entry per number: a Cameroonian NIU, a Senegalese NINEA, an EU VAT number. The list is available in live only.
https://api.wajub.com/customers/{id}/tax_idstypestringrequiredcm.niu, sn.ninea, ci.ifu, ke.pin, ng.tin, gh.tin, gh.vat, eu.vat, gb.vat or us.ein. Another type is accepted too, and only its length is checked.valuestringrequiredcountry_codestringrequiredCM.Add the number from your server, with a live secret key:
curl https://api.wajub.com/customers/cus_sAaim5apjocIgtlhzJY3wQ8s/tax_ids \
-H "Authorization: $WAJUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "cm.niu",
"value": "M123456789000L",
"country_code": "CM"
}'The number comes back pending, and Wajub checks it in the background. verification_status then
becomes verified when the value matches the format of its type, failed when it does not, and
unavailable when the check could not run. The formats are the ones your own registration number is
checked against (Tax configuration lists them): a Cameroonian
NIU is a letter, twelve digits and a letter. For an eu.vat number the check goes further and asks
the European VIES service whether the number exists.
Adding the same type and number again does not add a copy: the call answers 200 with the existing
entry, back to pending while it is checked again. That is how you retry an unavailable check.
GET /customers/{id}/tax_ids lists the numbers with their status, and
DELETE /customers/{id}/tax_ids/{tax_id} removes one.
Webhooks
Three events follow a customer, and they carry the full customer object as their payload.
| Event | Fires when |
|---|---|
customer.created | A customer is created, including one created implicitly by a payment |
customer.updated | Any field changes, including a status change or a merge from a payment |
customer.deleted | The customer is deleted and anonymised |
customer.created is noisier than it looks: it fires for every new payer your checkout sees, not
only for the ones you create yourself. Registration, verification and retries are covered in
Webhooks.
Related pages
- Customers API referenceEvery field on the resource, and the five generated endpoints.
- Shield RulesThe blocklist that actually refuses a payer, by email, phone, IP or BIN.
- WebhooksRegister an endpoint and verify the signature on customer events.
- PaginationPage numbers for a screen, cursors for an export.