WhatsApp API

Conversions

Report sales from your shop, billing system or CRM with POST /api/v1/conversions so the WhatsApp campaign that brought the customer gets credit. Safe to retry.

Overview

A conversion is a customer who bought after a campaign reached them. Report each sale from your own system with POST /api/v1/conversions and StartMessaging credits it to the campaign that last reached that customer on WhatsApp. The campaign’s report then shows how many customers it brought in, what their purchases were worth and its conversion rate.

Method & pathScopeWhat it does
POST /api/v1/conversionsconversions:writeReport a sale
  • Plan: conversions are part of the sales pipeline, on the Business and Enterprise plans and the free trial. On other plans the call answers 403 PLAN_FEATURE_REQUIRED with details.feature: "sales_pipeline".
  • Scope: create a key with the conversions:write scope in Developers → API keys. A key without it gets 403 INSUFFICIENT_SCOPE.
  • Write only: there is no endpoint to list conversions. They show up in each campaign’s report in the dashboard.

All paths are relative to the API host, for example https://waba-api.startmessaging.com/api/v1/conversions. Every request needs Authorization: Bearer sm_live_your_api_key.

How a sale is credited

  1. We match the phone you send to the customer’s WhatsApp chat.
  2. We look back from occurredAt for the last campaign message that was delivered or read on that chat, within your workspace’s conversion window.
  3. That campaign gets the credit. If no campaign reached the customer inside the window, the conversion is still recorded, with campaign: null.

The credit is decided once, when you report the sale. A campaign sent afterwards cannot claim it. A follow-up step and each run of a repeating campaign are campaigns of their own, so each earns its own credit.

The conversion window is 7 days unless you change it. Managers and above set it to anything from 1 to 30 days in Settings → Campaigns → Conversions. A change applies to sales recorded from then on.

A number does not have to be a contact. Someone a CSV campaign reached still counts: contactId is then null.

Pipeline stages count too. Besides sales your system reports, a lead moved from a stage that is not won into a pipeline stage marked as won counts as a conversion for the campaign that last reached them within the window (with no value; with no campaign when none did). The move can come from the dashboard, from PATCH /api/v1/contacts/{idOrPhone} with pipelineStageId, or from a chatbot flow. Moving the lead out of every won stage deletes that conversion again. Those conversions have source: "pipeline"; the ones you report have source: "api".

Report a sale

POST /api/v1/conversions

FieldDescription
phoneRequired. The customer’s phone number, up to 32 characters. E.164 (+ and country code) is safest; a number without a country code is read against your workspace’s country.
orderIdYour own reference for the sale, up to 100 characters. Send it and the call is safe to repeat (see below).
valueWhat the sale was worth, in currency. Zero or more, with at most two decimal places.
currencyA 3-letter ISO 4217 code for value, such as INR or USD. Defaults to INR. Stored only with a value.
occurredAtWhen the sale happened, as an ISO 8601 date-time. Defaults to now. Not in the future, and not more than a year ago.
  • New sale: 201 Created, created: true.
  • Same orderId again: 200 OK, created: false, with the conversion already recorded, credited as it was the first time. The new body is ignored, so a retry never counts a sale twice or moves its credit.

Without orderId, every call records a new conversion. Send an orderId, or at least an Idempotency-Key header, so a retried request cannot count the sale twice. See Pagination & Idempotency.

curl -X POST "https://waba-api.startmessaging.com/api/v1/conversions" \
  -H "Authorization: Bearer sm_live_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "phone": "+919812345678",
    "orderId": "ORD-10442",
    "value": 2499.50,
    "currency": "INR",
    "occurredAt": "2026-10-12T10:30:00+05:30"
  }'
const res = await fetch("https://waba-api.startmessaging.com/api/v1/conversions", {
  method: "POST",
  headers: {
    Authorization: "Bearer sm_live_your_api_key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    phone: "+919812345678",
    orderId: "ORD-10442",
    value: 2499.5,
    currency: "INR",
    occurredAt: "2026-10-12T10:30:00+05:30",
  }),
});
const { data } = await res.json(); // res.status is 201 (new) or 200 (already reported)
console.log(data.created, data.conversion.campaign?.name ?? "no campaign");
import requests

res = requests.post(
    "https://waba-api.startmessaging.com/api/v1/conversions",
    headers={"Authorization": "Bearer sm_live_your_api_key"},
    json={
        "phone": "+919812345678",
        "orderId": "ORD-10442",
        "value": 2499.50,
        "currency": "INR",
        "occurredAt": "2026-10-12T10:30:00+05:30",
    },
)
data = res.json()["data"]
print(res.status_code, data["created"], data["conversion"]["campaign"])

Response (201 Created):

{
  "data": {
    "conversion": {
      "id": "5b2f9d3e-1c4a-4e7b-8f6d-2a3b4c5d6e7f",
      "source": "api",
      "contactId": "3f0c2a52-8a51-4c0e-9d0a-2b7f8f3a9c11",
      "phone": "+919812345678",
      "orderId": "ORD-10442",
      "value": 2499.5,
      "currency": "INR",
      "occurredAt": "2026-10-12T05:00:00.000Z",
      "campaign": { "id": "9a7e5c1b-3f2d-4e8a-b6c0-1d2e3f4a5b6c", "name": "Diwali offer" },
      "touchedAt": "2026-10-10T04:30:12.000Z"
    },
    "created": true
  },
  "meta": { "requestId": "...", "timestamp": "2026-10-12T05:00:01.000Z" }
}
FieldDescription
idConversion id.
sourceapi for a sale reported here, pipeline for a lead moved to a won stage.
contactIdThe contact with this number, or null when there is none.
phoneThe number, in E.164.
orderId, value, currencyAs you sent them, or null. currency is null when there is no value.
occurredAtWhen the sale happened.
campaignThe campaign credited with the sale (id and name), or null when none reached this number within the window.
touchedAtWhen the credited campaign’s message went, or null.

What the campaign report shows

Each campaign’s report in the dashboard has a Conversions section:

  • Customers: conversions credited to the campaign, from both sources.
  • Value: the total of the reported sales that had a value, per currency. A won pipeline stage counts as a customer but adds no value.
  • Conversion rate: customers out of the people the campaign was delivered to.

The campaign’s recipient list and CSV export also show when each number converted.

Errors

StatusCodeWhen
400MOBILE_INVALIDphone is not a phone number.
400VALIDATION_ERRORA missing phone, a negative value or one with more than two decimals, a currency that is not 3 letters, or an occurredAt in the future or more than a year ago.
402SUBSCRIPTION_REQUIREDThe workspace’s subscription has ended and it can only read.
403PLAN_FEATURE_REQUIREDThe plan does not include the sales pipeline (details.feature: "sales_pipeline"). Conversions are on Business, Enterprise and the free trial.
403INSUFFICIENT_SCOPEThe key lacks conversions:write.
409IDEMPOTENCY_KEY_IN_USEA request with the same Idempotency-Key is still being processed.
422IDEMPOTENCY_KEY_REUSEDThe Idempotency-Key was used before with a different body.
429RATE_LIMIT_EXCEEDEDToo many requests this minute. Wait for Retry-After.

See Errors & Limits for the error format.

FAQ

Do I have to send a value? No. Without value the sale still counts as a customer for the campaign; it just adds nothing to the campaign’s value total.

What if the customer was never reached by a campaign? The conversion is recorded with campaign: null. Nothing is credited.

Can I report a sale from last month? Yes, with occurredAt, up to a year back. The credit goes to the campaign that reached the number within your conversion window before that time.

I sent the same order twice with a different value. Which one counts? The first. A repeated orderId returns the stored conversion with 200 and changes nothing.

Where do I set the conversion window? In Settings → Campaigns → Conversions, from 1 to 30 days (7 by default). Managers and above can change it.

Which plan do I need? Business or Enterprise, or the free trial. Conversions are part of the sales pipeline.