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"], whichcontact.updateincludes
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
| Value | Meaning |
|---|---|
interested | The contact was interested |
not_interested | The contact was not interested |
callback | The contact asked to be called back |
dnq | The contact did not qualify |
language_barrier | The contact could not follow the call in its language |
do_not_call | The contact asked not to be called again |
transferred | The call was put through to a person |
hung_up | Someone answered and hung up before saying who they were |
wrong_number | The number is not the contact's |
deceased | Whoever answered said the contact has died |
voicemail | The call went to voicemail |
straight_to_voicemail | The call went to voicemail without ringing (usually a phone off) |
no_answer | Nobody answered |
declined | The contact declined the call |
busy | The line was busy |
invalid_number | The network reported that the number does not exist |
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:
| Event | Source | What it becomes |
|---|---|---|
| Last call | Any call, and the campaign | at, if it is later than the current value |
| Last call: the outcome | Any call, and the campaign | at, if it is later than the current value |
| Call count | Any call, and the campaign | One 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
{
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
{
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.
In Dripcel, generate an API key with only the
contact.update.eventspermission.In DialCru, go to Manage β Webhooks β New webhook:
URL:
https://api.dripcel.com/contacts/call-outcomesEvent:
call_outcome.submitted. Narrow it to the campaigns or outcomes you want, or send every one.Custom header:
Authorizationwith the valueBearer <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 }}
}
Send a test delivery. It carries an example number (
+27829876543), so Dripcel answersunmatchedand 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.