Campaigns

Create a buyer (Beta)

[!CAUTION] Beta. This endpoint is still being built: more options are being added, and what it accepts may change.

Not available yet, and still set in the application:

  • Google Sheets delivery
  • Static fields
  • Ping caps and cap notifications
  • Creating or editing buyer groups (listing them and assigning a buyer to one are supported)
  • Deleting, cloning or importing a buyer
  • Testing a buyer
  • Reading back post conditions, payloads and headers (write-only)

Add a buyer to a campaign: which client it is, what it pays per lead, and how leads are delivered to it.

A new buyer starts paused, as it does in the application, so its setup can be checked before leads flow. Resume it with the pause & resume endpoint.

Delivery types

delivery.typeWhat it doesNeeds
lead_storekeeps the leads in Lead Prosper for the buyer to collectnothing else; any other delivery property answers 422
direct_postsends each lead to the buyer's endpoint as it arrivesurl
ping_postasks the buyer first (ping), then sends the lead (post)ping.url and post.url
one_to_onea ping-post whose answers list several buyers, on a one-to-one campaignping.url, post.url and the buyers mapping on both
emailemails each leadrecipients

An HTTP request

A direct_post is one request, written directly under delivery. A ping_post is two, under delivery.ping and delivery.post, each with the same properties. Only the url is required; what is left out gets the application's defaults:

PropertyDefaultRules
urlnonehttp or https. {{field_name}} tags are allowed in the path and the query string, not in the host. Private and loopback addresses are refused
methodPOSTGET, POST or PUT. A GET sends the payload in the query string: its payload_type is form (or left out) and it takes no headers; anything else answers 422
payload_typeformform, json or xml
payloadgeneratedMust parse as its payload type. Generated as one {{field_name}} tag per campaign field, as the application's "generate payload" button does
headersContent-Type for the payload typeA name holds letters, digits, -, _ and . only. Content-Type is added when none is given
timeout301 to 120 seconds
response_typetexttext, json or xml

The deprecated <> tag syntax is refused in the url, the payload and the headers; write tags as {{field_name}}.

Reading the buyer's answer

response_mapping says what the answer looks like when the lead was accepted and when it was duplicated. A ping has accepted only.

response_typeHow a rule is read
textthe body is searched for value, case-insensitive. Defaults: accepted and duplicate
json, xmlkey is a path in the body, compared with value by condition. All three are required
conditionMeaningkey
equalthe key equals the value. Matches true, false and null tooa path in the body
containthe key contains the valuea path in the body
response_hasthe whole body contains the valuefixed: body. Leave it out
http_status_codethe HTTP status equals the valuefixed: http_status_code. Leave it out

Real-time pricing

With real_time_price on, the amount the buyer pays is read out of its answer at real_time_price_key. It needs a json or xml response, and pricing.static must not be sent with it: the price is saved as 0 and comes from the answer.

On a direct post and on the post leg it is refused where the price is decided another way: a highest-bidder campaign, a buyer in a highest-bidder group, a ping-post exchange, and a one-to-one campaign. The ping leg is not restricted.

Post conditions (ping-post)

delivery.post_conditions holds conditions the ping's answer must meet before the post is sent. Optional: without them the post is always sent. They need a json or xml ping response.

Groups are joined by match (any or all); so are the rules inside a group.

Rule typeComparesConditionsTakes
fielda lead fieldeq, neq, gt, gte, lt, lte, between, one_of, containsfield: a campaign field name, lp_subid1 or lp_subid2; value
ping_responsea key in the ping's answerthe same ninefield: the key; value
supplierthe supplier the lead came fromeq, neq, one_ofsupplier_ids, from suppliers[].id
pricethe price the ping returnedeq, gt, gte, lt, lte, betweenvalue. Only with real-time pricing on the ping

between takes two values, one_of a list, every other condition one. Post conditions are write-only for now: no response publishes them.

One-to-one

Only on a one-to-one campaign. A ping and a post, as a ping_post takes them, plus a buyers object on each that says where the answer lists the buyers:

PropertyOnRequiredMeaning
list_keyping, postyesthe list of buyers in the answer
bid_id_keyping, postyesa bid's id, inside each item
price_keypingyesa bid's price, inside each item
tcpa_name_keypingyesthe buyer's TCPA name, inside each item
max_buyerspingno, default 0 (no limit)the most bids taken from one answer. Only when the campaign sells shared

The bids are priced by the ping's answer, so a one-to-one buyer takes no pricing.static, pricing.rules, pricing.bid_penalty, pricing.bid or pricing.supplier_margins, no real_time_price and no post_conditions. pricing.margin_override applies.

Customized API response (one-to-one campaigns)

api_response adds keys to each bid the supplier receives, on any buyer of a one-to-one campaign. A list of {"name": "...", "value": {"type": "...", "value": "..."}, "ping": true, "post": true}, replaced as a whole when sent; null removes it.

value.typeThe key's value isDelivery types
staticvalue.value itselfall
ping_responseread from the ping's answer at value.valueping_post, one_to_one
post_responseread from the post's answer at value.value. Returned on the post onlydirect_post, ping_post, one_to_one
ping_response_buyers, post_response_buyersread from each buyer's item in the answerone_to_one

id, price, execution_time and buyer_id are in every bid already and cannot be names. tcpa_name is the one predefined key: it needs a value, is returned on both the ping and the post, takes static (and ping_response or post_response on a ping_post), and is not available on a one_to_one buyer, which reads it at buyers.tcpa_name_key. Changing a buyer to one_to_one removes it.

Email

PropertyDefaultRules
recipientsnone1 to 5 addresses, no duplicates
subjectNew lead from {{lp_campaign_name}}may carry tags
from_namethe platform's sender name
reply_tothe platform's reply-to addressa valid address
payloadgeneratedthe email body; one name: {{name}} line per field when omitted

Conditional prices

pricing.rules is a list of conditional prices. For each lead they are checked in order: the first whose rules match sets the price, and pricing.static applies when none does. Not accepted with real-time pricing, and turning real-time pricing on removes them.

"pricing": {
  "static": 10,
  "rules": [
    {"price": 25, "match": "all", "rules": [
      {"type": "field", "field": "state", "condition": "one_of", "value": ["CA", "NY"]},
      {"type": "field", "field": "loan_amount", "condition": "gte", "value": "50000"}
    ]},
    {"price": 15, "match": "any", "rules": [
      {"type": "day_time", "windows": ["sat|12:00 AM|12:00 AM|America/New_York", "sun|12:00 AM|12:00 AM|America/New_York"]}
    ]}
  ]
}

Here a California or New York lead asking for 50,000 or more pays 25, any other lead on a weekend pays 15, and everything else pays 10.

KeyMeaning
pricewhat the lead pays when this item matches
price_from_fieldinstead of price: read it from a lead field of type numeric (any other type answers 422). A lead with no number there falls through to the next item
matchall (default): every rule must match. any: one is enough
rulesat least one rule, of the types below

Rule types

typeMatches whenTakesExample
fieldthe lead's value in a field meets the conditionfield: a campaign field name, lp_subid1 or lp_subid2; condition; value{"type": "field", "field": "state", "condition": "eq", "value": "CA"}
supplierthe lead came from this supplier. Buyers onlysupplier_id, from suppliers[].id{"type": "supplier", "supplier_id": 779}
day_timethe lead arrives inside one of the windowswindows{"type": "day_time", "windows": ["mon|09:00 AM|05:00 PM|America/New_York"]}

With match: all a field can be used once per item.

Conditions (field rules)

conditionMatches when the lead's valuevalueExample
eqequals the valueone{"field": "state", "condition": "eq", "value": "CA"}
neqdiffers from the value, or is emptyone{"field": "state", "condition": "neq", "value": "CA"}
gtis greater than the valueone{"field": "loan_amount", "condition": "gt", "value": "50000"}
gteis the value or greaterone{"field": "credit_score", "condition": "gte", "value": "700"}
ltis less than the valueone{"field": "loan_amount", "condition": "lt", "value": "10000"}
lteis the value or lessone{"field": "debt", "condition": "lte", "value": "5000"}
betweenis from the first value to the second, both includedtwo{"field": "credit_score", "condition": "between", "value": ["600", "699"]}
one_ofis any value in the lista list{"field": "zip_code", "condition": "one_of", "value": ["90210", "10001"]}
excludeis none of the values in the list. An empty value does not matcha list{"field": "state", "condition": "exclude", "value": ["FL", "TX"]}
containscontains the text anywhereone{"field": "email", "condition": "contains", "value": "@gmail."}
blankis empty or missingnone{"field": "phone", "condition": "blank"}
nblankhas any valuenone{"field": "phone", "condition": "nblank"}

Text comparisons ignore case. An empty value matches only blank and neq.

Which conditions a field takes depends on its type, from the campaign's fields:

Field typeConditionsvalue
text (and lp_subid1, lp_subid2)eq, neq, one_of, exclude, contains, blank, nblanktext; on a field with options, one of its options
numericeq, neq, gt, gte, lt, lte, between, one_of, exclude, blank, nblanknumbers
datethe same as numericdates in the field's own format
date of birtheq, neq, gt, gte, lt, lte, between, blank, nblankage in years: {"field": "date_of_birth", "condition": "gte", "value": "18"} is 18 or older

Day and time

day_time windows are written as a DAY_HOUR filter's (see Delivery schedule windows): day|start|end|timezone, 12-hour times on the quarter hour, one timezone for all windows, no overlaps. Two differences: a window cannot run past midnight (split it at 12:00 AM into two days, fri|10:00 PM|12:00 AM|... and sat|12:00 AM|02:00 AM|...), and start equal to end is the whole day.

Bid override (highest bidder)

On a highest-bidder campaign or in a highest-bidder buyer group, pricing.bid changes where the buyer stands when buyers are ordered by bid. It never changes what the buyer pays.

pricing.bid.modeTakesThe buyer is ordered as if its bid were
customprice, optional rules (same shape as pricing.rules)the amount entered. Refused when identical to the buyer's price
bump_uppercentage, above 0 and up to 1000its bid plus the percentage
bump_downpercentage, above 0 and up to 100its bid minus the percentage
positionposition, 1 to 999at that place in line, 1 being first

"bid": null inside pricing turns it off. The bid penalty is a separate setting, applied on top of the override; it has no effect with position.

Supplier-specific margins (ping-post exchange)

pricing.supplier_margins sets a margin on this buyer's bids for chosen suppliers: {"enabled": true, "suppliers": [{"supplier_id": 779, "margin": 40}]}. It has its own switch, independent of pricing.margin_override, and the list is kept while it is off. A margin for a supplier wins over that supplier's own margin override.

Settings that depend on the campaign

Each is refused with 422 on a campaign where the application does not show it.

PropertyApplies on
pricing.bid_penaltya highest-bidder campaign, a buyer in a highest-bidder group, or a ping-post exchange that is not one-to-one
pricing.margin_overridea ping-post exchange or a one-to-one campaign
pricing.supplier_marginsa ping-post exchange
delivery.type: one_to_one, api_responsea one-to-one campaign
pricing.bida highest-bidder campaign, or a buyer in a highest-bidder group
buyer_group_ida campaign that distributes by buyer groups; ids from GET /v2/campaigns/{campaign}/buyer-groups. A buyer in no group receives no leads

Full examples

One complete body per delivery type. Field names such as state and phone are the campaign's own; use yours.

Lead store

{
  "client_id": 50,
  "nickname": "Acme Buyer - Lead store",
  "pricing": {"static": 8},
  "delivery": {"type": "lead_store"}
}

Direct post, paying 20 for California and New York leads and 12.50 for the rest:

{
  "client_id": 50,
  "nickname": "Acme Buyer - Direct post",
  "pricing": {
    "static": 12.5,
    "rules": [
      {"price": 20, "rules": [{"type": "field", "field": "state", "condition": "one_of", "value": ["CA", "NY"]}]}
    ]
  },
  "delivery": {
    "type": "direct_post",
    "url": "https://example.com/leads?source={{lp_supplier_id}}",
    "method": "POST",
    "payload_type": "json",
    "payload": "{\"first_name\": \"{{first_name}}\", \"last_name\": \"{{last_name}}\", \"phone\": \"{{phone}}\", \"state\": \"{{state}}\", \"zip\": \"{{zip_code}}\"}",
    "headers": [{"name": "Authorization", "value": "Bearer abc123"}],
    "timeout": 30,
    "response_type": "json",
    "response_mapping": {
      "accepted": {"key": "status", "condition": "equal", "value": "ok"},
      "duplicated": {"condition": "response_has", "value": "duplicate"}
    }
  }
}

Ping post. The buyer answers the ping with {"accepted": true, "ping_id": "…", "bid": 12}; the post is sent only when bid is 10 or more, and carries the ping_id back:

{
  "client_id": 50,
  "nickname": "Acme Buyer - Ping post",
  "pricing": {"static": 15},
  "delivery": {
    "type": "ping_post",
    "ping": {
      "url": "https://example.com/ping",
      "payload_type": "json",
      "payload": "{\"state\": \"{{state}}\", \"zip\": \"{{zip_code}}\"}",
      "timeout": 10,
      "response_type": "json",
      "response_mapping": {
        "accepted": {"key": "accepted", "condition": "equal", "value": "true"}
      }
    },
    "post": {
      "url": "https://example.com/post",
      "payload_type": "json",
      "payload": "{\"ping_id\": \"{{lp_ping_response:ping_id}}\", \"first_name\": \"{{first_name}}\", \"last_name\": \"{{last_name}}\", \"phone\": \"{{phone}}\"}",
      "timeout": 30,
      "response_type": "json",
      "response_mapping": {
        "accepted": {"key": "status", "condition": "equal", "value": "ok"},
        "duplicated": {"key": "status", "condition": "equal", "value": "duplicate"}
      }
    },
    "post_conditions": {
      "groups": [
        {"rules": [{"type": "ping_response", "field": "bid", "condition": "gte", "value": "10"}]}
      ]
    }
  }
}

Email

{
  "client_id": 50,
  "nickname": "Acme Buyer - Email",
  "pricing": {"static": 5},
  "delivery": {
    "type": "email",
    "recipients": ["leads@example.com", "sales@example.com"],
    "subject": "New lead from {{lp_campaign_name}}",
    "from_name": "Acme Leads",
    "reply_to": "support@example.com",
    "payload": "Name: {{first_name}} {{last_name}}\nPhone: {{phone}}\nState: {{state}}"
  }
}
post/v2/campaigns/{campaign}/buyers

Request

  • Base URL: https://api.leadprosper.io/public
  • URL: https://api.leadprosper.io/public/v2/campaigns/{campaign}/buyers
  • Auth: one of:
    • HTTP bearer
    • API key in query parameter api_token

Path parameters

campaigninteger required

Campaign id.

Request body

client_idinteger required

A client on the account.

nicknamestring nullable

What the buyer is called on this campaign. Falls back to the client's company when empty.

buyer_group_idinteger nullable

The buyer group to put the buyer in, from GET /v2/campaigns/{campaign}/buyer-groups. null leaves it in none. Only on a campaign that distributes by buyer groups.

Example request

{
  "client_id": 50,
  "nickname": "Acme Buyer - Direct post",
  "pricing": {
    "static": 12.5,
    "rules": [
      {
        "rules": [
          {
            "windows": [
              "mon|09:00 AM|05:00 PM|America/New_York"
            ]
          }
        ]
      }
    ],
    "bid": {
      "rules": [
        {
          "rules": [
            {
              "windows": [
                "mon|09:00 AM|05:00 PM|America/New_York"
              ]
            }
          ]
        }
      ]
    }
  },
  "api_response": [
    {
      "name": "source"
    }
  ],
  "delivery": {
    "url": "https://example.com/leads",
    "headers": [
      {
        "name": "Authorization",
        "value": "Bearer …"
      }
    ],
    "real_time_price_key": "data.price",
    "response_mapping": {
      "accepted": {
        "key": "status",
        "value": "ok"
      },
      "duplicated": {
        "key": "status",
        "value": "ok"
      }
    },
    "ping": {
      "url": "https://example.com/leads",
      "headers": [
        {
          "name": "Authorization",
          "value": "Bearer …"
        }
      ],
      "real_time_price_key": "data.price",
      "response_mapping": {
        "accepted": {
          "key": "status",
          "value": "ok"
        }
      },
      "buyers": {
        "list_key": "bids",
        "bid_id_key": "id",
        "price_key": "price",
        "tcpa_name_key": "name"
      }
    },
    "post": {
      "url": "https://example.com/leads",
      "headers": [
        {
          "name": "Authorization",
          "value": "Bearer …"
        }
      ],
      "real_time_price_key": "data.price",
      "response_mapping": {
        "accepted": {
          "key": "status",
          "value": "ok"
        },
        "duplicated": {
          "key": "status",
          "value": "ok"
        }
      },
      "buyers": {
        "list_key": "bids",
        "bid_id_key": "id"
      }
    }
  }
}

Response

Success

statusstring

Changes

No recorded changes to this operation across all 1 revision of this API.