WatificationDocs

COD confirmations

Ask customers to confirm orders, change delivery times, and explain delivery refusals.

Create a cash-on-delivery confirmation from your store backend with POST /cod-confirmations. Read the current answers with GET /cod-confirmations/{confirmationId} or receive signed webhooks.

How the flow works

Submit a confirmation request

Send the customer's phone number, sender ID, template, parameters, and order reference. HTTP 202 accepts the request; it does not mean the customer has answered.

The customer receives a WhatsApp message

The confirmation template has three quick-reply buttons in this order: confirm, cancel, change time. Your template supplies the visible button labels.

The customer answers

A confirm or cancel tap records the answer. If configured, one reminder resends the template while waiting for an answer. No answer before the configured deadline records no_response.

Collect a changed delivery time

A change-time tap asks for a delivery day, then a time range. Typed text during rescheduling completes the flow with a note and any choices already collected. Follow-up steps expire 23 hours after the customer's last message; expiry completes with partial answers, so date and time range can be null.

Receive the outcome

Watification sends an outcome webhook to each enabled endpoint. Later taps on the original template can change completed answers and produce another event. Compare data.occurredAt when applying updates.

Template requirements

Both templates must be APPROVED, category UTILITY, and standard templates on the sender's WhatsApp Business account. Use a text header or no header; media headers are unsupported. No buttons other than the required quick replies are allowed.

TemplateButton positions
ConfirmationExactly 3: confirm, cancel, change time.
Refusal checkExactly 2: yes (I refused), no (I did not).

The API assigns actions by position. Ensure your button text matches those actions.

Dashboard setup checklist

Open Sales > COD confirmation > Overview and complete the setup items:

  1. Set your delivery time zone: open Settings, choose an IANA Time zone, and save.
  2. Add follow-up texts for a language: configure texts for the exact template language. Include Day question, Time question, Refusal reason question, Reasons list button label, Other label, and Note prompt.
  3. Approve a confirmation template on a WhatsApp number: use Create template to submit a ready-made English or Arabic template, or use your own compatible template. Wait for approval and refresh Overview.
  4. Approve a refusal template if you will use refusal checks.
  5. Create an API key and enable a webhook endpoint under Developers.

In Settings, use Reminder and Mark as no response after for each check type. Reminders are off by default; deadlines default to 1,440 minutes. Reminder values are 15-4,320 minutes, deadlines are 60-10,080 minutes, and a deadline must be later than its reminder. Timings start when the request's message submission completes, rather than when WhatsApp reports delivery.

Configure Delivery days offered (1-3 starting tomorrow, with up to 6 weekdays skipped), 1-3 time-range labels of at most 20 characters, and 1-9 refusal reasons of at most 24 characters per language. The system adds the other-reason choice. Closing message (optional) is sent at most once per check on reply completion when configured; timer expiry does not send it. Select Save settings.

Create a confirmation

Set WATIFICATION_API_KEY on your server and replace all fictional values. The example uses the ready-made cod_confirm template's four positional body parameters: customer, order, total, and address.

POST /cod-confirmations
curl --fail-with-body --request POST \
  'https://api.watification.com/api/v1/cod-confirmations' \
  --header "Authorization: Bearer $WATIFICATION_API_KEY" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: order-example-1042' \
  --data '{
    "to": "+15555550123",
    "senderId": "11111111-1111-4111-8111-111111111111",
    "template": { "name": "cod_confirm", "language": "en" },
    "parameters": { "body": ["Alex", "1042", "45 USD", "123 Example Street"] },
    "reference": "1042"
  }'
FieldRequiredValue
toYesValid phone with leading +, normalized to E.164.
senderIdYesWatification sender UUID in your workspace.
template.nameYesNon-empty template name.
template.languageYesExact language code with configured follow-up texts.
parametersNoValues matching the template; omit when none are needed.
parameters.headerIf neededString, present exactly when the text header has a parameter.
parameters.bodyIf neededPositional string array or named string object.
referenceYesOrder reference, 1-128 characters; not unique.

Body parameters must match the template's count or exact names. Each header/body value must contain non-whitespace text and be at most 1,024 characters. Do not supply button parameters. Unknown fields or query parameters are rejected.

Ready-made confirmation template parameters
{
  "parameters": {
    "body": ["Alex", "1042", "45 USD", "123 Example Street"]
  }
}

HTTP 202 returns:

Acceptance response
{
  "confirmationId": "33333333-3333-4333-8333-333333333333",
  "status": "accepted",
  "reference": "1042"
}

Read answers and status

GET /cod-confirmations/{confirmationId}
curl --fail-with-body \
  'https://api.watification.com/api/v1/cod-confirmations/33333333-3333-4333-8333-333333333333' \
  --header "Authorization: Bearer $WATIFICATION_API_KEY"

HTTP 200 returns:

Rescheduled confirmation
{
  "confirmationId": "33333333-3333-4333-8333-333333333333",
  "reference": "1042",
  "to": "+15555550123",
  "status": "rescheduled",
  "answeredAt": "2026-10-09T10:05:00.000Z",
  "reschedule": {
    "date": "2026-10-10",
    "timeRange": "9 AM - 1 PM",
    "note": null
  },
  "refusalCheck": null,
  "failure": null,
  "createdAt": "2026-10-09T10:00:00.000Z"
}
FieldMeaning
confirmationId, reference, toConfirmation UUID, your order reference, and recipient.
statusConfirmation state from the table below.
answeredAtISO timestamp or null; partial follow-up expiry can set this timestamp.
reschedulenull or { date, timeRange, note }; each value may be null. Date is YYYY-MM-DD in your delivery time zone.
refusalChecknull or { status, reason, note, answeredAt }; answer values can be null.
failurenull or { code, message } for the most recently failed confirmation or refusal check.
createdAtISO creation timestamp.
Confirmation statusMeaning
acceptedReserved; initial submission not complete.
awaiting_answerSubmitted and waiting for a customer tap.
confirmedCustomer confirmed.
cancelledCustomer cancelled.
reschedulingCollecting a delivery day or time range.
rescheduledReschedule completed, possibly with partial choices or a note.
no_responseInitial answer deadline passed. A later tap can still update it.
failedSubmitted initial or reminder message failed while awaiting an answer.

Check a reported delivery refusal

Start at most one refusal check per confirmation. The original confirmation must exist in your workspace, have a conversation, and be neither accepted nor failed. The check uses its original sender and recipient.

POST /cod-confirmations/{confirmationId}/refusal-check
WATIFICATION_BASE_URL='https://api.watification.com/api/v1'
CONFIRMATION_ID='33333333-3333-4333-8333-333333333333'
curl --fail-with-body --request POST \
  "$WATIFICATION_BASE_URL/cod-confirmations/$CONFIRMATION_ID/refusal-check" \
  --header "Authorization: Bearer $WATIFICATION_API_KEY" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: refusal-example-1042' \
  --data '{
    "template": { "name": "cod_refusal", "language": "en" },
    "parameters": { "body": ["Alex", "1042"] }
  }'

The request accepts only template and optional parameters, with the same rules as creation. HTTP 202 returns:

Refusal-check acceptance response
{
  "confirmationId": "33333333-3333-4333-8333-333333333333",
  "refusalCheck": { "status": "accepted" }
}

If the customer says yes, the flow asks for a reason from a list. The other-reason choice asks for a typed note. Typed text during either the reason or note step also completes with a note. Follow-up expiry completes with the collected parts, possibly a null reason.

Refusal statusMeaning
acceptedReserved; refusal submission not complete.
awaiting_answerWaiting for yes/no.
awaiting_reasonCustomer said yes; waiting for a reason.
awaiting_noteWaiting for an other-reason note.
deniedCustomer said they did not refuse.
refusal_confirmedReason/note step completed, including partial expiry.
no_responseInitial refusal-answer deadline passed.
failedRefusal initial or reminder message failed while awaiting an answer.

Idempotency, quotas, and events

Both POST routes accept an optional Idempotency-Key of at most 128 characters. Use a different non-empty key per logical operation. Matching repeats return the original acceptance response without another message or quota unit. All request fields are compared after phone/UUID normalization; named object key order does not matter. Changed values return 409 IDEMPOTENCY_KEY_REUSED. Keys are workspace-scoped within each operation family.

Confirmation creation and refusal checks have separate monthly quotas. Each accepted request consumes one unit from its own allowance. Repeats, reminders, follow-up questions, and closing messages consume no additional COD quota. Later failures are not refunded. Zero or exhausted allowance returns 429 COD_QUOTA_EXCEEDED. See Sales > COD confirmation > Overview for allowances and Log for answers.

Confirmation events: cod.confirmed, cod.cancelled, cod.reschedule_requested, cod.no_response, cod.failed. Refusal events: cod.refusal_confirmed, cod.refusal_denied, cod.refusal_no_response, cod.refusal_failed. Each contains the GET response fields plus data.occurredAt. Starting a reschedule or reason question does not itself emit an outcome. See the event catalog.

Endpoint errors

See rate limits and errors for recovery and Problem Details.

HTTPCodesMeaning
401API_KEY_INVALIDMissing, invalid, or revoked key.
404COD_SENDER_NOT_FOUND, COD_TEMPLATE_NOT_FOUND, COD_CONFIRMATION_NOT_FOUNDRequested resource not found in this workspace.
409COD_NOT_CONFIGUREDDelivery time zone not configured.
409COD_LANGUAGE_NOT_CONFIGUREDTemplate language lacks follow-up texts.
409COD_SENDER_UNAVAILABLE, COD_TEMPLATE_NOT_USABLESender or template cannot be used.
409COD_CONFIRMATION_NOT_ELIGIBLEOriginal confirmation cannot start a refusal check.
409COD_REFUSAL_CHECK_EXISTSA refusal check already exists.
409IDEMPOTENCY_KEY_REUSEDKey matches a different request.
422COD_TEMPLATE_PARAMETERS_INVALIDParameter shape, count, names, or values do not match.
422DEVELOPER_INVALID_INPUT, RECIPIENT_BLOCKEDInvalid input or blocked recipient.
429COD_QUOTA_EXCEEDED, RATE_LIMITEDMonthly allowance or per-key request limit reached.

On this page