Campaigns
REST API reference for campaigns
This reference is generated from the Invitebase OpenAPI contract.
Referral campaigns — name, status, and reward configuration. The qualification gate (what counts as a referral) is built in the dashboard's Campaign Builder, not through the API.
List campaigns
Authentication
Authorization: Bearer <secret key>.
Parameters
| Name | In | Required | Schema | Description |
|---|---|---|---|---|
limit | query | No | {"type":"integer","minimum":1,"maximum":100,"default":25} | |
starting_after | query | No | {"type":"string"} | A cursor (object id) for pagination; returns the page after this object. |
Responses
200
A paginated list of campaigns.
application/json schema:
{
"allOf": [
{
"$ref": "#/components/schemas/ListResponse"
},
{
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Campaign"
}
}
}
}
]
}401
Missing or invalid API key.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}403
The API key is valid but is not permitted to call this endpoint.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}429
Too many requests.
Response headers:
{
"Retry-After": {
"description": "Seconds until the caller may retry.",
"schema": {
"type": "integer",
"minimum": 1
}
}
}application/json schema:
{
"$ref": "#/components/schemas/Error"
}Create a campaign
Creates the campaign shell: name, status, and reward configuration. The qualification gate is built in the dashboard's Campaign Builder — the API does not accept a gate definition.
Authentication
Authorization: Bearer <secret key>.
Parameters
| Name | In | Required | Schema | Description |
|---|---|---|---|---|
Idempotency-Key | header | No | {"type":"string"} | A unique key for one logical mutation. The same key and request replay the original result for 24 hours; using the key with a different request returns 409. |
Request body
Required.
application/json schema:
{
"$ref": "#/components/schemas/CampaignCreate"
}Responses
201
Campaign created.
application/json schema:
{
"$ref": "#/components/schemas/Campaign"
}400
The request was malformed or invalid.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}401
Missing or invalid API key.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}403
The API key is valid but is not permitted to call this endpoint.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}429
Too many requests.
Response headers:
{
"Retry-After": {
"description": "Seconds until the caller may retry.",
"schema": {
"type": "integer",
"minimum": 1
}
}
}application/json schema:
{
"$ref": "#/components/schemas/Error"
}Retrieve a campaign
Authentication
Authorization: Bearer <secret key>.
Parameters
| Name | In | Required | Schema | Description |
|---|---|---|---|---|
campaignId | path | Yes | {"type":"string"} |
Responses
200
The campaign.
application/json schema:
{
"$ref": "#/components/schemas/Campaign"
}401
Missing or invalid API key.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}403
The API key is valid but is not permitted to call this endpoint.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}404
The requested resource does not exist.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}429
Too many requests.
Response headers:
{
"Retry-After": {
"description": "Seconds until the caller may retry.",
"schema": {
"type": "integer",
"minimum": 1
}
}
}application/json schema:
{
"$ref": "#/components/schemas/Error"
}Update a campaign
Partial update. Archived campaigns are terminal: updating one returns a 400. The account's plan applies to the campaign's effective reward, not only a reward in the request: while an active campaign's stored money reward is outside the plan (for example after a downgrade), any update that leaves it active returns 400 reward_not_in_plan until reward is replaced, and pausing or archiving always succeeds. A stored reward the plan resolves (a monetary reward without categories, or cash without cash_methods) is stored resolved.
Authentication
Authorization: Bearer <secret key>.
Parameters
| Name | In | Required | Schema | Description |
|---|---|---|---|---|
campaignId | path | Yes | {"type":"string"} | |
Idempotency-Key | header | No | {"type":"string"} | A unique key for one logical mutation. The same key and request replay the original result for 24 hours; using the key with a different request returns 409. |
Request body
Required.
application/json schema:
{
"$ref": "#/components/schemas/CampaignUpdate"
}Responses
200
Updated campaign.
application/json schema:
{
"$ref": "#/components/schemas/Campaign"
}400
The request was malformed or invalid.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}401
Missing or invalid API key.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}403
The API key is valid but is not permitted to call this endpoint.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}404
The requested resource does not exist.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}429
Too many requests.
Response headers:
{
"Retry-After": {
"description": "Seconds until the caller may retry.",
"schema": {
"type": "integer",
"minimum": 1
}
}
}application/json schema:
{
"$ref": "#/components/schemas/Error"
}Archive a campaign
Soft delete: the campaign stops accepting new referrals but stays readable, so existing referrals keep their definition. Idempotent.
Authentication
Authorization: Bearer <secret key>.
Parameters
| Name | In | Required | Schema | Description |
|---|---|---|---|---|
campaignId | path | Yes | {"type":"string"} | |
Idempotency-Key | header | No | {"type":"string"} | A unique key for one logical mutation. The same key and request replay the original result for 24 hours; using the key with a different request returns 409. |
Responses
204
Campaign archived.
401
Missing or invalid API key.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}403
The API key is valid but is not permitted to call this endpoint.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}404
The requested resource does not exist.
application/json schema:
{
"$ref": "#/components/schemas/Error"
}429
Too many requests.
Response headers:
{
"Retry-After": {
"description": "Seconds until the caller may retry.",
"schema": {
"type": "integer",
"minimum": 1
}
}
}application/json schema:
{
"$ref": "#/components/schemas/Error"
}Schemas
Schema references below resolve against these definitions. Required fields, enums, bounds, and composed types are preserved from the contract.
The ListResponse object
| Field | Type | Required | Description |
|---|---|---|---|
object | string | Yes | |
has_more | boolean | Yes | |
data | array | Yes |
Full schema:
{
"type": "object",
"required": [
"object",
"data",
"has_more"
],
"properties": {
"object": {
"type": "string",
"const": "list"
},
"has_more": {
"type": "boolean"
},
"data": {
"type": "array",
"items": {}
}
}
}The Campaign object
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | |
status | string | No | |
reward | array | Yes | One reward configuration per recipient — a single entry (the referrer) is single-sided; adding a referred_user entry makes the campaign double-sided. Recipients must be distinct. |
velocity_controls | array | No | Validation-time anti-farming limits that work alongside the AI fraud decision. Each rule counts the current referral plus prior validations in its sliding window. ip_subnet uses an app-scoped HMAC of a coarse click network; raw IP addresses are never stored. device means Invitebase's first-party web click cookie, not a browser or native-device fingerprint. flag records evidence but pays normally; block rejects before reward release. There is no manual-review hold. |
id | string | Yes | |
object | string | Yes | |
mode | Mode | No | Whether the object exists in test or live mode (determined by the API key). |
livemode | boolean | Yes | |
created_at | string | Yes |
Full schema:
{
"allOf": [
{
"$ref": "#/components/schemas/CampaignCreate"
},
{
"type": "object",
"required": [
"id",
"object",
"livemode",
"created_at"
],
"properties": {
"id": {
"type": "string",
"example": "camp_123"
},
"object": {
"type": "string",
"const": "campaign"
},
"mode": {
"$ref": "#/components/schemas/Mode"
},
"livemode": {
"type": "boolean"
},
"created_at": {
"type": "string",
"format": "date-time"
}
}
}
]
}The qualification gate
The qualification gate — what counts as a referral — is built in the Campaign Builder, not through the API. There's no gate JSON to send or receive: the API doesn't accept a gate definition on create or update, and campaign responses don't include one. To see how a referral is progressing against its gate, read gate_progress on the referral. See Qualification gates for how gates work.
The CampaignCreate object
The campaign shell. The qualification gate is not part of the API — it is built in the dashboard's Campaign Builder after creation.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | |
status | string | No | |
reward | array | Yes | One reward configuration per recipient — a single entry (the referrer) is single-sided; adding a referred_user entry makes the campaign double-sided. Recipients must be distinct. |
velocity_controls | array | No | Validation-time anti-farming limits that work alongside the AI fraud decision. Each rule counts the current referral plus prior validations in its sliding window. ip_subnet uses an app-scoped HMAC of a coarse click network; raw IP addresses are never stored. device means Invitebase's first-party web click cookie, not a browser or native-device fingerprint. flag records evidence but pays normally; block rejects before reward release. There is no manual-review hold. |
Full schema:
{
"type": "object",
"required": [
"name",
"reward"
],
"description": "The campaign shell. The qualification gate is not part of the API — it is built in the dashboard's Campaign Builder after creation.\n",
"properties": {
"name": {
"type": "string",
"example": "Refer a friend — give a month, get a month"
},
"status": {
"type": "string",
"enum": [
"active",
"paused"
],
"default": "active"
},
"reward": {
"type": "array",
"minItems": 1,
"maxItems": 2,
"items": {
"$ref": "#/components/schemas/RewardConfig"
},
"description": "One reward configuration per recipient — a single entry (the referrer) is single-sided; adding a `referred_user` entry makes the campaign double-sided. Recipients must be distinct.\n"
},
"velocity_controls": {
"type": "array",
"maxItems": 12,
"default": [],
"description": "Validation-time anti-farming limits that work alongside the AI fraud decision. Each rule counts the current referral plus prior validations in its sliding window. `ip_subnet` uses an app-scoped HMAC of a coarse click network; raw IP addresses are never stored. `device` means Invitebase's first-party web click cookie, not a browser or native-device fingerprint. `flag` records evidence but pays normally; `block` rejects before reward release. There is no manual-review hold.\n",
"items": {
"$ref": "#/components/schemas/VelocityControl"
}
}
}
}The RewardConfig object
How one recipient of a validated referral is rewarded. A campaign's reward is an array of these, one per recipient. Validated at write time: unknown fields are rejected, money methods require amount + currency, free_access requires free_access_duration, and money rewards must be included in the account's plan (otherwise 400 reward_not_in_plan). Delivery is separate from method: the same free_access reward can arrive over your webhook or as a code, without changing what the recipient is promised.
| Field | Type | Required | Description |
|---|---|---|---|
method | string | Yes | What the recipient gets — never how it reaches them, which is delivery. in_app and free_access are app grants with no money movement. The rest are money rewards paid through our payout partner, drawn from the prefunded balance: a store gift_card, a prepaid_visa card, a charity donation, or cash (see cash_methods). monetary lets the recipient choose among categories at claim time. Plans gate categories: Indie offers gift_card and charity; prepaid_visa and cash need Growth or Enterprise, which offer all four. The former single-rail methods paypal, venmo and ach are rejected — use cash with cash_methods. |
categories | array | No | monetary only. The categories the recipient may choose from. Omit it to offer every category your plan includes; the stored campaign shows the resolved list. |
cash_methods | array | No | For cash, or monetary offering cash. The cash options the recipient may pick: PayPal, Venmo, Cash App, bank transfer, or debit card. Omit it to offer every cash option your plan includes. |
recipient | string | Yes | Who this entry rewards. Add one configuration per side to make a campaign double-sided. |
amount | integer | No | Value in smallest currency unit (for money methods) or app-defined units. |
currency | Currency | No | ISO 4217 currency code (lowercase), e.g. "usd". |
free_access_duration | object | No | Required for method = free_access. The term your referral pages advertise — e.g. { "count": 3, "unit": "month" }. For code deliveries this is the advertised term only: the enforced one is baked into the code you supply, so generate codes that match. |
delivery | string | No | For method = free_access — how the grant reaches the recipient. webhook (the default) calls your backend over the signed reward webhook. manual, api, and app_store_connect hand over a code from campaign inventory, and an active campaign using one of them requires stocked inventory. revenuecat grants the entitlement through your RevenueCat project. |
in_app_payload | object | No | For method = in_app — what to grant (e.g. grant the gold skin). |
milestone_threshold | integer | No | Reward only after the referrer reaches N validated referrals (e.g. 5). |
cap_per_referrer | integer | No | Max rewards per referrer (fraud control). |
cap_per_month | integer | No | Max rewards per campaign per month (fraud control). |
expires_in_days | integer | No |
Full schema:
{
"type": "object",
"required": [
"method",
"recipient"
],
"additionalProperties": false,
"description": "How one recipient of a validated referral is rewarded. A campaign's `reward` is an array of these, one per recipient. Validated at write time: unknown fields are rejected, money methods require `amount` + `currency`, `free_access` requires `free_access_duration`, and money rewards must be included in the account's plan (otherwise 400 `reward_not_in_plan`). Delivery is separate from method: the same `free_access` reward can arrive over your webhook or as a code, without changing what the recipient is promised.\n",
"properties": {
"method": {
"type": "string",
"enum": [
"in_app",
"free_access",
"monetary",
"gift_card",
"prepaid_visa",
"charity",
"cash"
],
"description": "What the recipient gets — never how it reaches them, which is `delivery`. `in_app` and `free_access` are app grants with no money movement. The rest are money rewards paid through our payout partner, drawn from the prefunded balance: a store `gift_card`, a `prepaid_visa` card, a `charity` donation, or `cash` (see `cash_methods`). `monetary` lets the recipient choose among `categories` at claim time. Plans gate categories: Indie offers `gift_card` and `charity`; `prepaid_visa` and `cash` need Growth or Enterprise, which offer all four. The former single-rail methods `paypal`, `venmo` and `ach` are rejected — use `cash` with `cash_methods`.\n"
},
"categories": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/components/schemas/RewardCategory"
},
"description": "`monetary` only. The categories the recipient may choose from. Omit it to offer every category your plan includes; the stored campaign shows the resolved list.\n"
},
"cash_methods": {
"type": "array",
"minItems": 1,
"uniqueItems": true,
"items": {
"$ref": "#/components/schemas/CashMethod"
},
"description": "For `cash`, or `monetary` offering `cash`. The cash options the recipient may pick: PayPal, Venmo, Cash App, bank transfer, or debit card. Omit it to offer every cash option your plan includes.\n"
},
"recipient": {
"type": "string",
"enum": [
"referrer",
"referred_user"
],
"description": "Who this entry rewards. Add one configuration per side to make a campaign double-sided.\n"
},
"amount": {
"type": "integer",
"description": "Value in smallest currency unit (for money methods) or app-defined units."
},
"currency": {
"$ref": "#/components/schemas/Currency"
},
"free_access_duration": {
"type": "object",
"required": [
"count",
"unit"
],
"additionalProperties": false,
"description": "Required for method = free_access. The term your referral pages advertise — e.g. `{ \"count\": 3, \"unit\": \"month\" }`. For code deliveries this is the advertised term only: the enforced one is baked into the code you supply, so generate codes that match.\n",
"properties": {
"count": {
"type": "integer",
"minimum": 1,
"maximum": 120
},
"unit": {
"type": "string",
"enum": [
"day",
"week",
"month"
]
}
}
},
"delivery": {
"type": "string",
"enum": [
"webhook",
"manual",
"api",
"app_store_connect",
"revenuecat"
],
"default": "webhook",
"description": "For method = free_access — how the grant reaches the recipient. `webhook` (the default) calls your backend over the signed reward webhook. `manual`, `api`, and `app_store_connect` hand over a code from campaign inventory, and an active campaign using one of them requires stocked inventory. `revenuecat` grants the entitlement through your RevenueCat project.\n"
},
"in_app_payload": {
"type": "object",
"additionalProperties": true,
"description": "For method = in_app — what to grant (e.g. grant the gold skin)."
},
"milestone_threshold": {
"type": "integer",
"description": "Reward only after the referrer reaches N validated referrals (e.g. 5)."
},
"cap_per_referrer": {
"type": "integer",
"description": "Max rewards per referrer (fraud control)."
},
"cap_per_month": {
"type": "integer",
"description": "Max rewards per campaign per month (fraud control)."
},
"expires_in_days": {
"type": "integer"
}
}
}The RewardCategory object
A kind of money reward. Each is a product offered by our payout partner.
Full schema:
{
"type": "string",
"description": "A kind of money reward. Each is a product offered by our payout partner.",
"enum": [
"gift_card",
"prepaid_visa",
"charity",
"cash"
]
}The CashMethod object
How a cash reward reaches the recipient, through our payout partner: paypal, venmo, cash_app, bank (bank transfer), or debit (debit card).
Full schema:
{
"type": "string",
"description": "How a cash reward reaches the recipient, through our payout partner: `paypal`, `venmo`, `cash_app`, `bank` (bank transfer), or `debit` (debit card).\n",
"enum": [
"paypal",
"venmo",
"cash_app",
"bank",
"debit"
]
}The Currency object
ISO 4217 currency code (lowercase), e.g. "usd".
Full schema:
{
"type": "string",
"description": "ISO 4217 currency code (lowercase), e.g. \"usd\".",
"example": "usd"
}The VelocityControl object
| Field | Type | Required | Description |
|---|---|---|---|
subject | string | Yes | device is the random first-party click cookie from the hosted web page; Invitebase SDKs never derive or submit a device fingerprint. |
window_seconds | integer | Yes | Trailing sliding-window size, up to 30 days. |
threshold | integer | Yes | The next matching validation above this number trips the rule. |
action | string | Yes | flag records evidence; block rejects the referral and its reward. |
Full schema:
{
"type": "object",
"required": [
"subject",
"window_seconds",
"threshold",
"action"
],
"properties": {
"subject": {
"type": "string",
"enum": [
"referrer",
"ip_subnet",
"device"
],
"description": "`device` is the random first-party click cookie from the hosted web page; Invitebase SDKs never derive or submit a device fingerprint.\n"
},
"window_seconds": {
"type": "integer",
"minimum": 60,
"maximum": 2592000,
"description": "Trailing sliding-window size, up to 30 days."
},
"threshold": {
"type": "integer",
"minimum": 1,
"maximum": 10000,
"description": "The next matching validation above this number trips the rule."
},
"action": {
"type": "string",
"enum": [
"flag",
"block"
],
"description": "`flag` records evidence; `block` rejects the referral and its reward."
}
}
}The Mode object
Whether the object exists in test or live mode (determined by the API key).
Full schema:
{
"type": "string",
"enum": [
"test",
"live"
],
"description": "Whether the object exists in test or live mode (determined by the API key)."
}The Error object
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
Full schema:
{
"type": "object",
"required": [
"error"
],
"properties": {
"error": {
"type": "object",
"required": [
"type",
"code",
"message",
"doc_url"
],
"properties": {
"type": {
"type": "string",
"enum": [
"invalid_request_error",
"authentication_error",
"rate_limit_error",
"api_error"
]
},
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"param": {
"type": "string"
},
"doc_url": {
"type": "string",
"format": "uri"
}
}
}
}
}The CampaignUpdate object
Partial update — omitted fields keep their stored value. Archived campaigns are terminal and reject further updates.
| Field | Type | Required | Description |
|---|---|---|---|
name | string | No | |
status | string | No | |
reward | array | No | Replaces the whole array — send one entry to make the campaign single-sided again. |
velocity_controls | array | No | Replaces all validation-time velocity controls; send [] to disable them. |
Full schema:
{
"type": "object",
"description": "Partial update — omitted fields keep their stored value. Archived campaigns are terminal and reject further updates.\n",
"properties": {
"name": {
"type": "string"
},
"status": {
"type": "string",
"enum": [
"active",
"paused",
"archived"
]
},
"reward": {
"type": "array",
"minItems": 1,
"maxItems": 2,
"items": {
"$ref": "#/components/schemas/RewardConfig"
},
"description": "Replaces the whole array — send one entry to make the campaign single-sided again.\n"
},
"velocity_controls": {
"type": "array",
"maxItems": 12,
"description": "Replaces all validation-time velocity controls; send [] to disable them.",
"items": {
"$ref": "#/components/schemas/VelocityControl"
}
}
}
}