Skip to Content
IntegrationsAPI keys and records

Use the API

Use the API to read, create, and update program records from your server. Each key works for one program. Only the program owner can create or revoke keys.

Create a key

  1. Open API keys in your dashboard.
  2. Enter a name, such as Billing server.
  3. Choose Create API key.
  4. Choose Copy API key and save it on your server.
  5. Close the window. The full key cannot be shown again.

Do not put the key in browser code, a public repository, or a screenshot. You can keep up to 20 active keys. Choose Revoke key when you no longer use one. Removing the owner’s access also stops their keys.

API keys in the dashboard

Make a request

The base path is /api/v1 on your Referral Bear site. Send the key in the Authorization header.

curl "$REFERRAL_BEAR_URL/api/v1/partners?limit=10" \ -H "Authorization: Bearer $REFERRAL_BEAR_API_KEY"

Set REFERRAL_BEAR_URL to your site’s origin and REFERRAL_BEAR_API_KEY to the saved key. A list response has success, data, has_more, and total_count.

Each record includes organization_id, taken from its program. Linked records use the same organization ID.

Read records

PathRecordsExtra filters
/programsThe key’s programNone
/partnersAffiliatesgroup_id, email, status
/customersReferred customerspartner_id, email, status, search
/linksReferral linkspartner_id, param (or parameter), value
/commissionsCommissionspartner_id, customer_id, approval, settlement
/transactionsPaymentspartner_id, customer_id
/promotion-codesCoupon codespartner_id, group_id, code

Add a record ID to any path except /programs to read one record. For example, use /partners/RECORD_ID. Its response has success and data.

Use GET to read these routes. Create and update actions are listed below. Supported delete actions are listed below. Most Tolt field aliases are not available yet. Use Referral Bear record IDs. Imported source IDs are not route IDs. The key’s program is used by default. If you send program_id, it must match the key.

Coupon records include active, type, and value. A percentage code uses type: "percentage" and a value such as 20 for 20% off. A fixed code uses type: "fixed" and an amount in the currency’s smallest unit, such as 2500 for $25. The original percent_off, amount_off, and currency fields are also included when saved. If an older code has missing or conflicting amounts, type and value are null. The organization_id comes from the program.

Add expand[] to include a related record in the same response. This works when reading one record or a list.

PathValues for expand[]
/partnersgroup, program
/customerspartner, program
/promotion-codespartner, program

For example, /customers?expand[]=partner&expand[]=program includes each customer’s affiliate and program. Add other filters or page cursors as usual. Counts still refer to the customers, not their related records.

The related affiliate uses the same public fields as an affiliate read. Programs include their ID, name, slug, status, currency and creation date. Groups include their ID, name, description, privacy choice, website and creation date. Missing related records return null. Unrequested records are left out. Stats and other expansion types are not supported yet.

Filter and page

List routes, except /programs, accept these fields:

FieldValue
limit1 to 100. Default: 10.
orderasc or desc, sorted by creation date. Default: desc.
created_gteInclude records created at or after this date.
created_lteInclude records created at or before this date.
starting_afterThe last record ID from the page you just read.
ending_beforeThe first record ID from the page you just read.

Use a full date with a time zone, such as 2026-09-01T00:00:00Z. Keep the same filters and order as you move between pages. Use one cursor at a time. total_count counts every record that matches the filters. has_more tells you if more rows remain in the direction you requested.

Filters match exact values, except customer search. Email filters ignore letter case. For commissions, approval and settlement are separate fields. Use the values saved on the record. Amounts use the smallest currency unit, such as cents for USD. Private system fields, connection secrets and login details are not returned.

Search customer emails

Add search to find part of an email address. Letter case does not matter. For example, /customers?search=example.com finds customers whose email contains example.com. It does not search customer names.

Search checks up to 10,000 customers after applying the other filters. If more customers remain, the API returns 422. Add a date range or an affiliate filter, then try again. Counts include all matches in that filtered set. Keep the same search text when you move between pages.

MCP supports the same search in list_customers, export_data, and export_job_start. For either export tool, set resource to customers.

Handle errors

The limit is 25 requests per second for each key. A 429 response includes Retry-After: 1. Wait, then retry. A 401 means the key is missing, invalid, revoked, or its owner lost access. A 403 means the request names another program or the action is not allowed. A 404 means the record or route was not found. A 400 means a query field is invalid. Unknown filters are rejected.

Error responses have success: false and an error object with message and type. Read responses are not cached.

Create records

Use POST with a JSON body. All amounts are whole numbers in the currency’s smallest unit. For USD, 2500 means $25.00.

PathRequired fieldsOther fields
/partnersemail, and either name or first_name plus last_namegroup_id, status (pending or active), company_name, country_code, website
/customerspartner_id, email (or identifier)name, identifier, customer_id, link_id, status, created_at, lead_at, active_at, subscription_id, stripe_customer_id, promotion_code
/linkspartner_id, valueparam or parameter (default: ref), destination
/commissionsamount and either customer_id or partner_idcurrency (default: program currency), status or approval (default: pending), note
/promotion-codescode, type, value, partner_idduration, duration_in_months, sync_to_stripe
/transactionscustomer_id, amount, currency, occurred_at, billing_typenote, product_id, quantity, interval

Customer status can be lead, trialing, active, canceled or suspended. The default is lead. A supplied link must belong to that affiliate. Creating a lead can run the program’s signup flows.

lead_at records when the customer became a lead. active_at records activation. Send ISO 8601 dates, with a timezone. Dates cannot be in the future. On creation, the lead date defaults to created_at. A customer created as active also gets an activation date unless you supply one.

Payments record their payment time as the activation date. Subscription checks record when Referral Bear first sees an active subscription. Later events keep the earliest recorded date. Changing a status to canceled or suspended keeps the saved dates. Use the update API to correct a date or set a known date from your old tool. Tolt imports keep these dates when the export includes them. Older records may have no saved date.

A payment’s billing_type is one_time or subscription. Its date needs a time zone. A quantity needs a product ID. A manual payment runs the same fraud and commission rules as one entered in the dashboard. A manually created commission is separate from a payment. It does not run payment rules or send money.

For payments and commissions, send a UUID in the Idempotency-Key header. Reuse it only when retrying the same request. The first request returns 201. A repeat returns 200 and the same record. Changing the amount or other saved input while reusing the key returns 409.

curl -X POST "$REFERRAL_BEAR_URL/api/v1/commissions" \ -H "Authorization: Bearer $REFERRAL_BEAR_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: REPLACE_WITH_A_UUID" \ -d '{"partner_id":"REPLACE_WITH_CREATOR_ID","amount":2500,"currency":"USD","note":"Referral bonus"}'

For a new customer, you can send just email and partner_id. The email is saved in lowercase and used as the identifier. The name can be left out. Send identifier if your system uses a different ID; this also lets you create a record without an email.

Use customer_id for your app’s customer ID. It stays separate from the email and lookup identifier. You can change it with PUT. Old IDs stay reserved for that customer, so another referral cannot claim them. If no separate ID is saved, reads return the lookup identifier as customer_id.

For a new link, send param or parameter, not both. If you leave out destination, the link uses the affiliate group’s website. Set that website first or send a destination. Destinations must use HTTP or HTTPS and cannot contain login details.

A successful create response has success, data and created. Affiliate email addresses, customer identifiers and referral codes must be unique in the program. Repeating one of those creates returns 409. Read the existing record before trying again. JSON bodies are limited to 32 KB. Unknown fields are rejected.

Create a coupon code

Send POST to /api/v1/promotion-codes. Use type: "percentage" with a positive percentage up to 100, or type: "fixed" with a positive whole amount in the program currency’s smallest unit. For USD, 2500 means $25.

The default saves a coupon record. Create the matching discount in your payment provider too. To create it in your connected Stripe account, send sync_to_stripe: true.

Set duration to once (the default), forever, or repeating. Repeating discounts also need duration_in_months from 1 to 120. Other durations do not accept months.

Send a UUID in Idempotency-Key to retry the same create request without making another code. Keep the same body on retry. Coupon creation returns 201 with the saved record, including on a saved retry. Without a key, each request tries to create a new code. A code already used in the program returns 409.

Edit a coupon record

Use PUT /api/v1/promotion-codes/:id for a manually managed code. Send type, value, or both. Values use the same units as coupon creation. When switching discount types, send both fields so the amount is clear. Changing only type keeps the same number in the new units. For USD, changing 20% to fixed without a new value means 20 cents.

This changes the saved discount record. Update the discount in Stripe separately. Codes synced by Referral Bear cannot be edited through this route. The code name, affiliate, duration, payments and commissions stay the same. An edit is blocked while a status change or deletion is in progress.

Delete a coupon code

Send DELETE /api/v1/promotion-codes/:id with no body. The result has success: true and data: {"id": "YOUR_CODE_ID", "deleted": true}. Repeating the request returns the same result.

A synced Stripe code is disabled in Stripe before its Referral Bear record is removed. A code managed separately in Stripe is only removed from Referral Bear; manage that discount in Stripe too. Finish any pending sync or status change before deleting.

The code disappears from lists and the affiliate portal. Its old assignment stays saved for payment history and renewals. Existing payments and commissions are kept. The old code name and create-request ID cannot be reused for another code.

Update records

Use PUT /api/v1/partners/:id, PUT /api/v1/customers/:id, or PUT /api/v1/links/:id. Send only the fields you want to change. Other fields stay the same. A successful update returns HTTP 200 with the saved record.

RecordFields you can change
Affiliate (partners)name, first_name, last_name, status, group_id, country_code, company_name, website
Customercustomer_id, name, email, status, subscription_id, promotion_code, created_at, lead_at, active_at
Linkparam (or parameter), value, destination, disabled

Both customer creation and updates accept promotion_code. On creation, a code that cannot be applied still creates the customer under the supplied affiliate and returns the same warning.

Use promotion_code to attach a saved code assigned to the customer’s affiliate, their current group, or the program. A matching code returns promotion_code_id. If it cannot be applied, existing attribution stays unchanged and the response includes a promotion_code_not_applied warning. Other valid fields still update. This does not change the referring affiliate or recalculate past payments or commissions. Coupon customer totals retain previously linked codes; promotion_code_id identifies the latest code assigned through this API.

Customer dates accept ISO 8601 timestamps with a timezone and are saved in UTC. Future dates are rejected. Correcting created_at keeps payment records and other customer dates unchanged. The corrected date is used by later date-based flow checks; it does not recalculate past commissions.

Use first_name and last_name to keep names separate. Send both when creating an affiliate or adding them to a full-name record. An empty last_name is allowed. The full name, including the space between parts, can be up to 150 characters. Later, you can change either part. Do not send name with the separate fields. A full-name edit clears the separate fields. Older full-name records return null for both parts; we do not guess where to split a name.

Affiliate status can be pending, active, suspended, or rejected. Customer status can be lead, trialing, active, canceled, or suspended. Setting a customer to canceled or suspended keeps that choice when payment events arrive. Setting another status clears that override.

Use a two-letter country code, such as US. Groups must belong to the same program. You can clear a customer email or subscription ID with null.

curl -X PUT "https://referralbear.com/api/v1/links/YOUR_LINK_ID" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"param":"via","value":"jamie"}'

Old referral codes still point to the same link after an edit. Another affiliate cannot take them. A code already owned by another link returns HTTP 409. Send disabled: true to turn off a link.

Affiliate email changes, customer attribution changes, and payment edits are not part of these update routes yet. Unknown fields return HTTP 400.

Changing subscription_id replaces that one subscription in the customer’s saved list. Other imported subscriptions stay in the list. Clearing it removes only that one entry. Subscription IDs can come from any billing system. Use a non-empty identifier of up to 255 characters; a Stripe prefix is not required.

Use DELETE /api/v1/links/:id with your API key. No request body is needed. The response has success: true and data: {"id": "YOUR_LINK_ID", "deleted": true}. Repeating the deletion returns the same result.

The link leaves the dashboard, portal and API lists. Its codes stop accepting new clicks. Old codes stay reserved so another affiliate cannot take them. Existing clicks, referrals, payments and commissions keep their history. To turn a link off temporarily, update it with disabled: true instead.

Record a click from your server

Use POST /api/v1/clicks. Turn on tracking in your program first. Send either:

  • partner_id, with an optional link_id.
  • param and value, such as ref and jamie.

Do not mix these two methods. The affiliate must be active. Links must belong to that affiliate and be enabled.

curl -X POST "$REFERRAL_BEAR_URL/api/v1/clicks" \ -H "Authorization: Bearer $REFERRAL_BEAR_API_KEY" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: YOUR_REQUEST_UUID" \ -d '{"param":"ref","value":"jamie","page":"https://your-site.com/pricing","device":"desktop","country":"US"}'

Replace YOUR_REQUEST_UUID with a new UUID for each visit. Reuse it with the same API key and body when retrying that visit. The first request returns 201. A retry returns 200 with the same click. Tracking, the affiliate, and any linked referral link must still be enabled. Reusing the key for different data returns 409. Without this header, each request creates a new click.

You can include page, referrer, country, and device. The page must use one of your allowed tracking origins. Device can be desktop, mobile, or tablet. Country uses a two-letter code, such as US. Query strings and fragments are removed from saved page and referrer addresses. Paid tags and keywords in the page query are checked before removal. The referrer’s host is checked too.

The response’s data.id is the click ID. Use it as click_id when recording a signup, or send it in your payment provider’s referral field. expires_at is the expiry time in milliseconds. A click with blocked: true failed a fraud check and cannot earn a commission. If keyword rules are enabled, send the full page URL so they can be checked.

Delete a manual payment

Use DELETE /api/v1/transactions/:id for a manual payment that has not been refunded. Its linked commissions must be unpaid, unreversed, and outside a payout. Provider payments and locked records return 409.

curl -X DELETE "$REFERRAL_BEAR_URL/api/v1/transactions/YOUR_TRANSACTION_ID" \ -H "Authorization: Bearer $REFERRAL_BEAR_API_KEY"

Leave the body empty or send {}. A successful request returns 200 with data.id and data.deleted: true. Repeating the request returns the same result. An unknown ID returns 404.

The payment and its linked commissions leave the active records together. The audit archive keeps their details. The customer stays in the program. A retry of the original payment-creation request cannot bring back a deleted payment.

Change a manual commission

Use PUT /api/v1/commissions/:id to change a standalone manual commission. Send amount in minor units, note, or approval. You can use status instead of approval. The allowed values are pending, approved, and rejected.

curl -X PUT "$REFERRAL_BEAR_URL/api/v1/commissions/YOUR_COMMISSION_ID" \ -H "Authorization: Bearer $REFERRAL_BEAR_API_KEY" \ -H "Content-Type: application/json" \ -d '{"amount":1999,"status":"approved"}'

Fields you leave out stay the same. HTTP 200 returns the saved commission in data. You cannot change its currency or link it to a payment through this route.

Use DELETE /api/v1/commissions/:id to remove that manual commission. Send no body. The response is {"success":true,"data":{"id":"YOUR_COMMISSION_ID","deleted":true}}. Repeating the delete returns the same result. A deleted commission no longer appears in lists. Its archive and audit record are kept. Its original create request cannot be reused to bring it back.

Both actions require an unpaid manual commission with no refund and no linked payment. A linked customer is allowed. A commission already assigned to a payout returns HTTP 409. Remove it from the unpaid payout first. Commissions earned from payments cannot be edited or deleted through these routes yet.

Record a full refund

Use PUT /api/v1/transactions/:id/refund for a manual payment. Send no body, or an empty JSON object.

curl -X PUT "https://referralbear.com/api/v1/transactions/YOUR_TRANSACTION_ID/refund" \ -H "Authorization: Bearer YOUR_API_KEY"

This sets refunded_amount to the full payment amount and reverses its unpaid commissions. If part of the payment was already refunded, it records only the rest. Repeating the request does not add another refund. HTTP 200 returns the saved transaction in data.

This route records a refund; it does not send money to the customer. Refund Stripe payments in Stripe. Its payment events update Referral Bear. This route does not support Paddle payments. A payment with a paid commission or a commission in a payout returns HTTP 409. Remove unpaid commissions from their payout before recording a refund. Partial refunds are available in the dashboard.

Read through MCP

An MCP client can read your program at https://referralbear.com/api/mcp.

  1. Add that address as an MCP server in your client.
  2. Choose to connect. Referral Bear opens a page with the app name and return address.
  3. Enter a program API key from API keys in your dashboard.
  4. Choose Connect program to return to your client.

Only approve apps you trust. The connection can read records and create exports for that one program. Revoke its API key to stop access.

Clients that support a Bearer API key can also send it in the Authorization header, just as they would for the API.

The connection can list your program, affiliates, customers, transactions, commissions, and links. It can also look up one affiliate or customer, count program records, and list the fields available for exports. Each key stays limited to its own program. Use the tool list in your client to see the available filters.

List tools return up to 100 records per page. Amounts use integer minor units and include the currency. Revoke the API key to remove the client’s access.

MCP date filters accept a full timestamp or YYYY-MM-DD. A date alone means midnight UTC. For an upper bound, it does not include the rest of that day.

Use the export tool to read a page from one or all record types. Each list has its own next_cursor. Pass that cursor with the same record type, filters, and order to get the next page. A cursor cannot be used with all record types at once.

The affiliate performance tool shows customer counts, payments, commissions, and status totals. The reconciliation tool compares payments and commissions. It also counts payments with no linked commission and commissions whose payment is missing from the results.

These reports use payment amounts before refunds and commission amounts after refunds. Each currency has its own totals. A date filter uses the record’s creation date. A missing link can mean the related record falls outside your dates. Each report supports up to 10,000 matching rows per record type. Choose a smaller date range if you reach that limit.

Use the ranking tool to find top affiliates by revenue, commissions, clicks, or customers. Ranking revenue is after refunds. Date filters select affiliates by the date they joined; their totals still include all activity. Money uses your program’s currency. Set currency to read another currency’s totals.

The affiliate overview combines profile details, totals, and the first ten customers, payments, and commissions. Use the list tools to read more records. Rankings and overviews support up to 10,000 rows per record type.

For a larger export, use export_job_start. Save the job_id and use export_job_status to check progress. When its status is completed, use export_job_result to read the saved rows. Pass next_cursor as cursor to read another page. Keep using the same connection. Results expire after 24 hours. Token renewal keeps your connection and export access.

Two exports can run at once for each program. Set max_rows to choose the largest export you will accept, up to one million rows. The default is 200,000. An export fails if it exceeds that limit or detects records moving between its pages. Start a new export after the records stop changing. Failed jobs do not return partial results.

Create an MCP key and revoke its access.

Affiliate creation and updates accept valid two-letter ISO country_code values, such as US or GB. Lowercase codes are normalized; unknown codes are rejected.

Affiliate reads include rejected_at and suspended_at. Each date describes the current status. Leaving rejected or suspended clears its date. Repeating the same status keeps its saved date; older records without one return null.

Affiliate custom_fields contain question_id, question, answer_id, and answer. Multiple-choice answers are joined with commas. The answer ID stays the same for each affiliate and question. API reads and webhook events use the same fields.

Affiliate reads include payout_method and payout_details from the affiliate’s current account. No saved method returns none and an empty object. Reading payment details creates an audit entry. MCP exports include the method but leave out payment details.

Payment and commission reads support expand[]=customer, expand[]=partner, and expand[]=program. You can request all three together. Commissions include the linked payment’s revenue, source, and charge_id when the records match. Their status follows refunds and paid payouts. Payments also include saved product names and their source.

For a manual commission, customer_id selects its affiliate. If you also send partner_id, it must match the customer. Use status or approval with pending, approved, or rejected. The customer stays linked to the commission.