Sections
Theme

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

GET/v1/campaigns

Authentication

Authorization: Bearer <secret key>.

Parameters

NameInRequiredSchemaDescription
limitqueryNo{"type":"integer","minimum":1,"maximum":100,"default":25}
starting_afterqueryNo{"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

POST/v1/campaigns

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

NameInRequiredSchemaDescription
Idempotency-KeyheaderNo{"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

GET/v1/campaigns/{campaignId}

Authentication

Authorization: Bearer <secret key>.

Parameters

NameInRequiredSchemaDescription
campaignIdpathYes{"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

PATCH/v1/campaigns/{campaignId}

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

NameInRequiredSchemaDescription
campaignIdpathYes{"type":"string"}
Idempotency-KeyheaderNo{"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

DELETE/v1/campaigns/{campaignId}

Soft delete: the campaign stops accepting new referrals but stays readable, so existing referrals keep their definition. Idempotent.

Authentication

Authorization: Bearer <secret key>.

Parameters

NameInRequiredSchemaDescription
campaignIdpathYes{"type":"string"}
Idempotency-KeyheaderNo{"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

FieldTypeRequiredDescription
objectstringYes
has_morebooleanYes
dataarrayYes

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

FieldTypeRequiredDescription
namestringYes
statusstringNo
rewardarrayYesOne 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_controlsarrayNoValidation-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.
idstringYes
objectstringYes
modeModeNoWhether the object exists in test or live mode (determined by the API key).
livemodebooleanYes
created_atstringYes

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.

FieldTypeRequiredDescription
namestringYes
statusstringNo
rewardarrayYesOne 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_controlsarrayNoValidation-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.

FieldTypeRequiredDescription
methodstringYesWhat 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.
categoriesarrayNomonetary 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_methodsarrayNoFor 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.
recipientstringYesWho this entry rewards. Add one configuration per side to make a campaign double-sided.
amountintegerNoValue in smallest currency unit (for money methods) or app-defined units.
currencyCurrencyNoISO 4217 currency code (lowercase), e.g. "usd".
free_access_durationobjectNoRequired for method = free_access. The term your referral pages advertise — e.g. &#123; "count": 3, "unit": "month" &#125;. For code deliveries this is the advertised term only: the enforced one is baked into the code you supply, so generate codes that match.
deliverystringNoFor 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_payloadobjectNoFor method = in_app — what to grant (e.g. grant the gold skin).
milestone_thresholdintegerNoReward only after the referrer reaches N validated referrals (e.g. 5).
cap_per_referrerintegerNoMax rewards per referrer (fraud control).
cap_per_monthintegerNoMax rewards per campaign per month (fraud control).
expires_in_daysintegerNo

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

FieldTypeRequiredDescription
subjectstringYesdevice is the random first-party click cookie from the hosted web page; Invitebase SDKs never derive or submit a device fingerprint.
window_secondsintegerYesTrailing sliding-window size, up to 30 days.
thresholdintegerYesThe next matching validation above this number trips the rule.
actionstringYesflag 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

FieldTypeRequiredDescription
errorobjectYes

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.

FieldTypeRequiredDescription
namestringNo
statusstringNo
rewardarrayNoReplaces the whole array — send one entry to make the campaign single-sided again.
velocity_controlsarrayNoReplaces 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"
      }
    }
  }
}

What's next

On this page