Exchange Transactions
Most of these endpoints are for buyers — confirming, accepting and searching the leads sellers deliver against your offers. Sellers can also submit leads they collected themselves to an offer they run.
Create Lead Transaction(s)
As a buyer, use this endpoint to confirm leads you've received against a marketplace offer (e.g. a landing-page form completion). Each confirmed lead creates a lead transaction that credits the attributed seller.
- 🔐 Permissions:
["marketplace_transaction.create"]
Each lead references the send it came from using exactly one of campaign_id, send_id, or click_id (the same matchback identifiers used by sales uploads), plus a buyer-supplied resource_id used for audit/idempotency. Every lead must match back to a marketplace offer your organisation owns — leads that resolve to a non-marketplace campaign are rejected.
You can optionally set the status of each lead in the same call, accepting or rejecting it up front instead of creating it and then calling Update Transaction Status separately. See Setting the status on create.
POST Request
POST /exchange/buyer/transaction
Upload leads in bulk (1–1000 per request).
Body
type Body = {
/** Buyer-supplied unique id for this lead — used for audit/idempotency */
resource_id: string;
/** The cell number of the contact. Can be in local or international format (if local, the country of the corresponding campaign is used) */
cell: string;
/** The campaign ID this lead matches back to */
campaign_id?: string;
/** The send_id this lead matches back to */
send_id?: string;
/** The click_id this lead matches back to */
click_id?: string;
/**
* Accept, reject, or hold the resulting transaction as it is created.
* Defaults to the offer's configured default status.
*/
status?: "completed" | "pending" | "rejected";
}[];
Note: Provide exactly one of campaign_id, send_id, or click_id per lead — supplying none or more than one is rejected.
Example
[
{
"resource_id": "lead-0001",
"cell": "27112223333",
"campaign_id": "639978184d735c88ed8bbd68",
"status": "completed"
}
]
GET Request
GET /exchange/buyer/transaction/create
Confirm / postback a single lead via a GET request.
Params
The same values as used in a POST request (for a single lead) but now used as search parameters of the url, plus your API key.
type Params = {
/** Your account API key with the required permissions */
key: string;
/** Buyer-supplied unique id for this lead — used for audit/idempotency */
resource_id: string;
/** The cell number of the contact. Can be in local or international format */
cell: string;
/** The campaign ID this lead matches back to */
campaign_id?: string;
/** The send_id this lead matches back to */
send_id?: string;
/** The click_id this lead matches back to */
click_id?: string;
/**
* Accept, reject, or hold the resulting transaction as it is created.
* Defaults to the offer's configured default status.
*/
status?: "completed" | "pending" | "rejected";
};
Example
https://api.dripcel.com/exchange/buyer/transaction/create?key=KEY&resource_id=lead-0001&campaign_id=CAMPAIGN_ID&cell=27112223333&status=completed
Setting the status on create
By default a new lead transaction takes the offer's configured default status (either completed or pending). Supplying status overrides that per lead:
status | Effect |
|---|---|
completed | Accept the lead immediately. Credits the seller's wallet, deducts your credits, and counts toward the offer's caps. |
pending | Hold the lead for review. No wallet or credit movement until you accept it via Update Transaction Status. |
rejected | Reject the lead immediately. No wallet or credit movement. |
This saves a round trip: creating a lead and accepting it is one call rather than two.
The requested status is not always applied. Two protections take precedence:
- Duplicates. If the same contact already has a transaction against the same offer within the last 7 days, the transaction is created as
rejectedregardless of thestatusyou asked for. You are not charged for a duplicate lead. - Offer caps. If the offer has reached a configured cap, the transaction is held as
pendinginstead ofcompleted, so it can't spend past the cap. You can still explicitly sendrejectedto reject a capped lead outright.
To confirm the status a transaction actually landed on, use Search Transactions.
Response
Success
{
ok: true;
data: "Lead(s) submitted";
}
Error
{
ok: false;
// No matching marketplace offer for the given resource_id(s)
error: "No matching marketplace offer for: ...";
}
Update Transaction Status
As a buyer, use this endpoint to accept or reject a pending transaction.
If you already know the outcome when you submit the lead, you can skip this call and set the status directly on create instead — see Setting the status on create.
Request
PUT /exchange/buyer/transaction/:id/status
Body
type Body = {
status: "completed" | "rejected";
};
Search Transactions
Request
POST /exchange/buyer/transaction/search
Body
type Body = {
/** The transaction IDs to search for */
_id?: string | string[];
/** The transaction statuses to search for */
status?: "pending" | "completed" | "rejected";
/** Offer IDs to search for */
offer_id?: string | string[];
/** The date the transaction was created. */
createdAt?: { $gte?: string; $lte?: string };
}
Example
{
"_id": "63f5d0e0d9e9d1a0f0a3d8e1",
"status": "completed",
"offer_id": "63f5d0e0d9e9d1a0f0a3d8e1",
"createdAt": { $gte: "2023-02-14T09:02:51.000Z" }
}
Submit a Lead (Seller)
As a seller, use this endpoint to submit a lead you collected yourself — for example from your own landing page — to a buyer's offer that you run.
- 🔐 Permissions:
["marketplace_transaction.create"] - If your API key is limited to certain campaigns, it can only submit leads through those.
Ask your Dripcel account manager for an integration guide for the offer. It's a page made for your organisation that lists exactly the fields this offer's buyer needs, with an API key limited to your campaign for the offer and a ready-to-run example.
The offer's buyer has to allow this: only offers with Accept API leads switched on take leads from this endpoint. The offer must also pay per lead, be active and not paused by its schedule, you must be allowed to run it, it must be under its caps, and the buyer must have credit.
You submit the lead against your exchange campaign for the offer — the campaign you created to run it — so it is attributed and reported exactly like the opt-ins from your sends.
Request
POST /exchange/seller/lead
One lead per request.
Body
type Body = {
/** Your exchange campaign for the offer */
campaign_id: string;
/**
* Your unique id for this lead (e.g. your form submission id), up to 256
* characters. Used for idempotency: a resource_id you have already
* submitted is rejected, so retries are safe. It only has to be unique
* within your organisation.
*/
resource_id: string;
/** The lead. Only these standard contact fields are accepted. */
contact: {
/**
* Required. Local format is read against the offer's country (e.g.
* `0821234567` on a South African offer); otherwise use international
* format (e.g. `27821234567`).
*/
cell: string;
firstname?: string;
lastname?: string;
email?: string;
/** A 13-digit South African ID number */
idnumber?: string;
gross_income?: number;
household_income?: number;
/** ISO date, e.g. `1990-04-21` */
date_of_birth?: string;
car_ownership?: boolean;
gender?: string;
employment_status?: string;
job_title?: string;
province_or_state?: string;
city?: string;
postal_code?: string;
credit_score?: number;
lead_source?: string;
opt_in_status?: boolean;
preferred_contact_method?: string;
/** ISO date */
signup_date?: string;
customer_status?: string;
parental_status?: string;
relationship_status?: string;
education_level?: string;
home_ownership_status?: string;
};
/**
* Run every check and report the status the lead would get, without
* creating it or delivering it to the buyer. See Testing below.
*/
test?: boolean;
};
Required fields. Besides cell, the offer's buyer decides which fields are required: every field their API Lead hooks use without a fallback. A lead missing one is refused with invalid_lead, and the message names the missing fields. An integration guide lists them for you.
Fields with fixed options (gender, employment_status, lead_source, etc.) take the option keys listed under contact fields.
Any other field — a custom field, tags, or a combined name — is rejected rather than dropped: these fields are all the buyer receives, so you find out while integrating instead of the buyer finding a lead with missing data.
Example
{
"campaign_id": "639978184d735c88ed8bbd68",
"resource_id": "form-submission-1042",
"contact": {
"cell": "0821234567",
"firstname": "Thandi",
"lastname": "Mokoena",
"email": "[email protected]"
}
}
Testing
Add "test": true to the body to check a lead without submitting it. Every check runs (your key, the offer, the fields, a repeated resource_id, a duplicate cell, caps and the buyer's credit) and errors come back exactly as they would for a real lead. On success you get the status the lead would have had, but nothing is created and the buyer's hooks don't fire.
{
ok: true;
data: {
test: true;
/** "pending", or "rejected" if the cell would be a duplicate */
status: "pending" | "rejected";
};
}
What happens to the lead
- It waits for the buyer. A submitted lead is always created as
pending, whatever the offer's auto-approve setting. Your wallet is credited when the buyer accepts it, and nothing is paid if they reject it. - Duplicates are rejected. If the same cell already has a transaction on the offer from the last 7 days, the lead is created as
rejectedand is not delivered to the buyer. - The buyer receives it through their hooks. The buyer's API Lead hooks fire with the lead as
{{contact.*}}. - It counts toward the offer's caps while it is pending, just like any other lead.
Response
Success
{
ok: true;
data: {
/** The lead's exchange transaction */
transaction_id: string;
/** "pending", or "rejected" if the cell was a duplicate */
status: "pending" | "rejected";
};
}
Error
{
ok: false;
error: { code: string; message: string };
}
| Status | code | Meaning |
|---|---|---|
| 400 | invalid_lead | A contact field is unknown or invalid, or a field the offer requires is missing. The message names it. |
| 400 | invalid_msisdn | The cell is missing or isn't valid for the offer's country. |
| 400 | offer_not_per_lead | The offer pays per click or acquisition, not per lead. |
| 402 | offer_buyer_insufficient_balance | The buyer has no credit. Try again later. |
| 403 | offer_api_leads_disabled | The buyer hasn't switched on Accept API leads for this offer. |
| 403 | offer_inactive / offer_paused | The offer is inactive, or paused by its schedule. |
| 403 | offer_permission_denied | Your organisation may not run this offer. |
| 403 | insufficient_resource_ids | Your API key is limited to other campaigns. This error has a missing object instead of a message. |
| 404 | campaign_not_found | campaign_id isn't one of your exchange campaigns. |
| 409 | duplicate_resource | You have already submitted this resource_id. |
| 429 | offer_capped | The offer has reached a cap. Stop sending until it resets. |