Skip to main content

Call outcomes

The /contacts/call-outcomes endpoint lets a call centre tell Dripcel what came of its calls to your contacts: this contact, on this call campaign, had this outcome, at this time.

Each outcome is stamped on the contact as an event, the same way Dripcel stamps sends, clicks and replies, so you can then target campaigns with it. For example:

  • Not interested on a call in the last 30 days: don't send them the same offer by SMS.
  • Asked for a callback in the last 7 days: send a reminder SMS.
  • Not called in 30 days: send an SMS to win them back.
  • Called 3 or more times: leave them out.

You'll find these under Targeting β†’ Events in the campaign editor. Pick the action (for example Last call: Not interested), then a source: Any call, or one of your call campaigns.

Record call outcomes​

POST /contacts/call-outcomes

  • πŸ” Permissions: ["contact.update.events"], which contact.update includes

Send one outcome as a JSON object, which is what a webhook sends, or up to 1 000 as an array, for a backfill.

Body​

type CallOutcome = {
/**
* Your id for this outcome. Recommended: an outcome sent again with the same id
* (a webhook retried after a timeout, say) is recorded once. Without one, every
* request counts as another call. Up to 128 characters.
*/
id?: string;
/** The contact's cell number, in international format: "+27829876543" or "27829876543". */
cell: string;
/** What came of the call. One of the values below. */
outcome: Outcome;
/**
* The call campaign the call was part of, as your call centre knows it. Leave it
* out, or send `null` or `{ "id": null }`, for a call that was not part of one.
*/
campaign?: {
/** 1–64 letters, digits, "-" or "_". A UUID or an ObjectId both fit. */
id: string | null;
/** Shown when you pick the campaign in targeting. The latest one sent wins. */
name?: string;
} | null;
/** When the call's outcome was recorded, as ISO 8601. Defaults to now. Cannot be in the future. */
at?: string;
};

Outcomes

ValueMeaning
interestedThe contact was interested
not_interestedThe contact was not interested
callbackThe contact asked to be called back
dnqThe contact did not qualify
language_barrierThe contact could not follow the call in its language
do_not_callThe contact asked not to be called again
transferredThe call was put through to a person
hung_upSomeone answered and hung up before saying who they were
wrong_numberThe number is not the contact's
deceasedWhoever answered said the contact has died
voicemailThe call went to voicemail
straight_to_voicemailThe call went to voicemail without ringing (usually a phone off)
no_answerNobody answered
declinedThe contact declined the call
busyThe line was busy
invalid_numberThe network reported that the number does not exist
caution

do_not_call is recorded as an event only. It does not opt the contact out of your SMS or email campaigns. To stop messaging them as well, exclude the event in your targeting, or dedupe the contact through POST /contacts/update.

Example

{
"id": "2d4f6a80-3b5c-4d7e-8f9a-0b1c2d3e4f50",
"cell": "+27829876543",
"outcome": "callback",
"campaign": { "id": "7e8f9a0b-1c2d-4e3f-a4b5-c6d7e8f9a0b1", "name": "March renewals" },
"at": "2026-08-17T09:15:00.000Z"
}

What it records​

Each outcome sets, on the contact with that cell:

EventSourceWhat it becomes
Last callAny call, and the campaignat, if it is later than the current value
Last call: the outcomeAny call, and the campaignat, if it is later than the current value
Call countAny call, and the campaignOne more

A late or out-of-order outcome never moves a date backwards, but it is still counted.

A cell that is not one of your contacts is reported as unmatched, and nothing is stamped. Outcomes don't create contacts, and one that arrives before its contact is added is not applied later.

Response​

Success

200
{
ok: true;
data: {
recorded: number;
duplicate: number;
unmatched: number;
/** One per outcome sent, in the order sent. */
results: {
index: number;
id: string | null;
/**
* recorded: stamped on the contact
* duplicate: an outcome with this id was already recorded; nothing changed
* unmatched: you have no contact with this cell; nothing changed
*/
status: "recorded" | "duplicate" | "unmatched";
}[];
};
}

A duplicate or unmatched outcome is still a 200. Sending it again will not change the answer, so a webhook should not retry it.

Error

400
{
ok: false;
error: {
code: "invalid_call_outcome";
/** Which field, and why. In an array, the path starts with the item's index: "3.cell: Invalid cell number…" */
message: string;
};
}

If one outcome in an array is invalid, the whole request is refused and nothing is recorded.

Rate limit​

This endpoint has its own allowance of 100 requests per minute per organisation, separate from the rest of the API, so a busy call floor cannot use up your other integrations' requests. A request counts once however many outcomes it carries, so batch them if you send more. Over the limit you get a 429 with a Retry-After header.

From DialCru​

If your call centre runs on DialCru, you don't need any code: point a webhook at this endpoint.

  1. In Dripcel, generate an API key with only the contact.update.events permission.

  2. In DialCru, go to Manage β†’ Webhooks β†’ New webhook:

    • URL: https://api.dripcel.com/contacts/call-outcomes

    • Event: call_outcome.submitted. Narrow it to the campaigns or outcomes you want, or send every one.

    • Custom header: Authorization with the value Bearer <your Dripcel API key>

    • Body template:

      {
      "id": {{ outcome.id | json }},
      "cell": {{ contact.phone_e164 | json }},
      "outcome": {{ outcome.disposition | json }},
      "campaign": { "id": {{ campaign.id | json }} },
      "at": {{ outcome.submitted_at | json }}
      }
  3. Send a test delivery. It carries an example number (+27829876543), so Dripcel answers unmatched and stamps nothing. The test's campaign also stays out of your targeting list, because a call campaign only appears there once one of its outcomes matches a contact.

DialCru's webhook doesn't carry the campaign's name, so targeting lists the campaign by its id. To show a name, add "name" to the campaign object. Because the latest name sent wins, only do this on an endpoint filtered to that one campaign.