WhatsOS

INVOICE DELIVERY API

Your invoice link, delivered on WhatsApp

Your POS, website or billing platform creates and hosts the invoice. Its backend calls WhatsOS with the recipient’s phone number and invoice URL. WhatsOS sends an approved message containing the link to view or download that invoice, using your connected business number.

WhatsOS does not need access to your POS or platform. Your developer implements the API call on your side. PDF attachments are optional.

1. Configure once

  1. Create a sandbox project in Bills & Receipts.
  2. Open Templates and sync. Select demo_invoice_link and save it as the invoice-link template. For live delivery, select an approved utility text template with exactly one body variable for the URL, such as Your invoice is ready. View or download it here: {{1}}.
  3. Create an API key with messages:send and messages:read. Keep it on your backend.
  4. Enable project sending in Settings. Live delivery also requires an active service subscription and sender/template readiness. Existing valid workspace connections are reused.

No POS credentials, field mapping, documentation upload or vendor-specific connection is needed for this endpoint. Sandbox requests simulate delivery and never send a real WhatsApp message.

2. Call WhatsOS after the invoice is ready

POST https://whatsos.com/api/v1/messaging/invoices

{
  "to": "+919876543210",
  "invoice_url": "https://billing.example.com/invoices/1042",
  "reference": "INV-1042",
  "consent": {
    "obtained": true,
    "source": "Customer requested WhatsApp invoice at checkout"
  }
}

to is the recipient’s international phone number. invoice_url is your hosted HTTPS view/download URL, up to 1,024 characters. The invoice can be a web page or a PDF; a PDF file is not required. Use a customer-accessible link without browser login, cookies or authorization headers. Keep it valid long enough for the customer to use it.

reference is optional but recommended for invoice tracking. If omitted, WhatsOS derives it from the idempotency key. Record the customer’s permission in your own system and supply the consent fields.

Save the request as invoice.json:

curl https://whatsos.com/api/v1/messaging/invoices \
  -H "Authorization: Bearer $WHATSOS_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: invoice-1042-v1" \
  --data-binary @invoice.json

The API key determines the client project, connected WhatsApp number and saved template. You do not send Meta credentials or the template name with each invoice request.

Node.js example

import { WhatsosMessages } from './whatsos-messages.mjs';

const client = new WhatsosMessages({ apiKey: process.env.WHATSOS_API_KEY });
const result = await client.sendInvoice({
  to: invoice.customerPhone,
  invoice_url: invoice.viewUrl,
  reference: invoice.number,
  consent: { obtained: true, source: 'Checkout WhatsApp invoice consent' }
}, { idempotencyKey: invoice.whatsosOperationKey });

await saveMessageId(invoice.id, result.message_id);
const delivery = await client.get(result.message_id);

Persist whatsosOperationKey with the invoice before sending. Reuse the same key and unchanged request when retrying.

PHP example

$request = [
  'to' => $invoice['customer_phone'],
  'invoice_url' => $invoice['view_url'],
  'reference' => $invoice['number'],
  'consent' => ['obtained' => true, 'source' => 'Checkout WhatsApp invoice consent']
];
$ch = curl_init('https://whatsos.com/api/v1/messaging/invoices');
curl_setopt_array($ch, [
  CURLOPT_POST => true,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_TIMEOUT => 20,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . getenv('WHATSOS_API_KEY'),
    'Content-Type: application/json',
    'Idempotency-Key: ' . $invoice['whatsos_operation_key']
  ],
  CURLOPT_POSTFIELDS => json_encode($request)
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
// Store the message_id on HTTP 202. Retry timeouts with the same key.

3. Track delivery and handle retries

A successful request returns 202 Accepted with a message_id. This confirms queue acceptance, not customer delivery.

GET /api/v1/messaging/messages/{message_id}
Authorization: Bearer YOUR_API_KEY

Follow queued, submitting, accepted, sent, delivered and read statuses in the API or Delivery history. Signed callbacks can notify your backend of transaction events. Verify callback signatures using your project’s callback secret.

Optional PDF attachments and advanced messages

To send an actual PDF attachment, use POST /api/v1/messaging/messages with a PDF-header utility template and the document field. You can provide a hosted PDF URL or use the existing PDF upload endpoint. These are optional; the simple invoice-link endpoint does not require an upload.

{
  "to": "+919876543210",
  "kind": "invoice",
  "reference": "INV-1042-PDF",
  "template": {
    "name": "demo_invoice_pdf", "language": "en_US",
    "parameters": ["Alex", "INV-1042", "AED 250.00"]
  },
  "document": {"url": "https://billing.example.com/invoices/1042.pdf", "filename": "invoice.pdf"},
  "consent": {"obtained": true, "source": "Customer requested WhatsApp invoice"}
}

The advanced endpoint also supports text-only utility templates with the invoice HTTPS link in a body variable. Existing configured connectors continue to work, but new API callers do not need them.

Common responses

CodeNext step
401 INVALID_KEYCheck API key expiry or revocation.
403 MISSING_SCOPEUse a key with messages:send.
409 INVOICE_TEMPLATE_REQUIREDSync and save a one-variable utility text template in Templates.
403 MESSAGING_NOT_READYCheck project sending, template sync and service activation.
403 RECIPIENT_OPTED_OUTThe recipient must provide renewed permission.
409 IDEMPOTENCY_CONFLICT / REFERENCE_CONFLICTThe existing operation has different content. Do not blindly resend.
400 validation errorCheck the phone number, HTTPS URL, consent and idempotency header.
429 RATE_LIMITEDRetry later using the same operation key.