Skip to main content

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:

statusEffect
completedAccept the lead immediately. Credits the seller's wallet, deducts your credits, and counts toward the offer's caps.
pendingHold the lead for review. No wallet or credit movement until you accept it via Update Transaction Status.
rejectedReject 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 rejected regardless of the status you 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 pending instead of completed, so it can't spend past the cap. You can still explicitly send rejected to reject a capped lead outright.

To confirm the status a transaction actually landed on, use Search Transactions.

Response​

Success

201
{
ok: true;
data: "Lead(s) submitted";
}

Error

404
{
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.
tip

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.

200
{
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 rejected and 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

201
{
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 };
}
StatuscodeMeaning
400invalid_leadA contact field is unknown or invalid, or a field the offer requires is missing. The message names it.
400invalid_msisdnThe cell is missing or isn't valid for the offer's country.
400offer_not_per_leadThe offer pays per click or acquisition, not per lead.
402offer_buyer_insufficient_balanceThe buyer has no credit. Try again later.
403offer_api_leads_disabledThe buyer hasn't switched on Accept API leads for this offer.
403offer_inactive / offer_pausedThe offer is inactive, or paused by its schedule.
403offer_permission_deniedYour organisation may not run this offer.
403insufficient_resource_idsYour API key is limited to other campaigns. This error has a missing object instead of a message.
404campaign_not_foundcampaign_id isn't one of your exchange campaigns.
409duplicate_resourceYou have already submitted this resource_id.
429offer_cappedThe offer has reached a cap. Stop sending until it resets.