Developers

REST API reference and scopes

The OpenPromoter REST API v1: authentication with API keys and scopes, rate limits and every endpoint. Copy the requests and start building.

The REST API covers programs, partners, tracking, commissions, payouts, campaigns and Autopilot. The base URL is https://app.openpromoter.com/api/v1 or <APP_URL>/api/v1 on your own instance.

The machine-readable spec is at /api/openapi.json (OpenAPI 3.1). Import it into Postman, Insomnia or a client generator.

Authentication

Create a key in Settings > API & webhooks. The full key is shown once; OpenPromoter stores only a SHA-256 hash. Send it as a Bearer token:

curl https://app.openpromoter.com/api/v1/programs \
  -H "Authorization: Bearer $OPENPROMOTER_API_KEY"

A key belongs to one workspace and can be revoked at any time.

Scopes

Scope Allows
read Read everything
partners:write Create and update partners, links and codes; mint embed tokens; create campaigns; apply Autopilot suggestions
tracking:write Record clicks, leads, conversions and refunds
commissions:write Create commissions and bonuses
payouts:request Prepare payouts. A person still approves them in the dashboard

No scope can approve or send a payout.

Rate limits

600 requests per minute per key. Over the limit the API returns 429 with code rate_limited.

Errors

Errors return an HTTP status and a JSON body like { "error": { "code": "not_found", "message": "Program not found" } }. Validation errors also include issues (a list of { path, message }).

Status Code Meaning
400 invalid_request The body or query failed validation, or the action isn't allowed
401 unauthorized Missing or invalid API key
403 forbidden The key lacks the scope for this endpoint
404 not_found The program, partner or code does not exist in this workspace
409 conflict A unique value, such as a link key, is already taken
422 unparseable A campaign sentence could not be turned into a campaign
429 rate_limited Too many requests

Pagination

List endpoints return { "data": [...], "hasMore": true }. Pass limit (1 to 100, default 50) and before=<id of the last item> for the next page.

Money

Amounts are integers in minor units (cents for USD) with a three-letter currency. Conversion amounts exclude tax.

Endpoints

Method Path Scope Description
GET /events read Events (same payloads as webhooks) after a cursor with the oldest first. Used by openpromoter listen. Query: after (event id), limit (1 to 100, default 50)
GET /programs read List programs in the workspace
GET /programs/{programId}/stats read Totals for a period (revenue, commissions, clicks, signups, customers, MRR, owed). Query: days (1 to 730, default 30)
GET /programs/{programId}/partners read List partners with the newest first. Query: status (pending, approved, rejected, paused, banned), recruitedBy (a partner id: only the partners they recruited)
POST /programs/{programId}/partners partners:write Invite (create and approve) a partner. Body: email, name, website?, country?, recruiterCode? (two-tier: the code of the approved partner who recruited them)
GET /partners/{partnerId} read Get a partner with links
PATCH /partners/{partnerId} partners:write Change a partner's status or tier. Body: status?, groupId?
POST /partners/{partnerId}/links partners:write Create a short or deep link for a partner. Body: destination, subId?, key?
POST /programs/{programId}/embed-tokens partners:write Mint a 1-hour token for the embeddable partner dashboard. Creates (and approves) the partner if needed so customers can refer from inside your app. Body: email, name
GET /programs/{programId}/referrals read List referred signups and customers. Query: status (lead, trialing, active, canceled, refunded)
POST /clicks tracking:write Record a click server-side (e.g. from your own redirect). Body: programId, code, url?, visitorId?, ip?, userAgent?
POST /leads tracking:write Record a signup (lead) before payment. Body: programId, email, name?, clickId?, visitorId?, partnerCode?, customerId?
POST /conversions tracking:write Record a payment from a billing system without a built-in integration. Idempotent on paymentId
POST /refunds tracking:write Report a refund for a payment sent to /conversions. Body: programId, paymentId, refundId, refundedTotal, chargedTotal
GET /programs/{programId}/commissions read List commissions (the ledger). Query: status (pending, approved, held, paid, reversed), kind (sale, lead, bounty, bonus, adjustment, override), partnerId
POST /programs/{programId}/commissions commissions:write Add a manual commission or bonus (approved immediately). Idempotent with idempotencyKey. Body: partnerId, amount, description
GET /programs/{programId}/payouts read List payouts. Query: status (awaiting_approval, processing, paid, failed, canceled)
POST /programs/{programId}/payouts payouts:request Prepare payouts for partners over the minimum. They wait for a person to approve them in the dashboard; no money moves. Body: partnerIds?
GET /programs/{programId}/campaigns read List campaigns
POST /programs/{programId}/campaigns partners:write Create a campaign from one sentence (e.g. "double commissions for annual plans this month"). Body: prompt, launch (default false)
GET /programs/{programId}/autopilot read Autopilot suggestions and recent actions
POST /programs/{programId}/autopilot/{actionId}/apply partners:write Apply an Autopilot suggestion (payout suggestions only prepare a batch)
POST /programs/{programId}/ask read Ask a plain-language question about the program. Body: question

Recording a conversion

curl https://app.openpromoter.com/api/v1/conversions \
  -H "Authorization: Bearer $OPENPROMOTER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "programId": "prg_123",
    "paymentId": "inv_981",
    "customerId": "cus_42",
    "amount": 4900,
    "currency": "usd",
    "interval": "month",
    "email": "jo@example.com",
    "clickId": "clk_8f2k"
  }'
Field Type Notes
programId string Required
paymentId string Required. Your unique id for this payment; sending it twice is safe
customerId string Required. Your customer id; renewals with the same id stay with the same partner
amount integer Required. Minor units excluding tax
currency string Required. Three letters such as usd
interval month or year Optional. Marks the payment as recurring
email string Optional. Matches an earlier lead
clickId string Optional. window.openpromoter.clickId
partnerCode string Optional. Credit a partner by code
promoCode string Optional. A partner coupon code used at checkout
paidAt ISO 8601 Optional. Defaults to now

The response is { "duplicate": false, "effects": [...] }. effects lists what changed, such as the commission that was booked.

SDK

The TypeScript SDK wraps every endpoint with types.

Edit this page on GitHub