Atribu
API Reference

Conversions & Revenue

Conversion counts, daily trends, attributed revenue, and cash collected by source.

Four endpoints for querying conversion activity and revenue attribution. All require the conversions:read scope.

Revenue means cash only

Revenue in all endpoints below refers exclusively to confirmed payments from Stripe and MercadoPago. GHL pipeline values (deal stages, closed-won projections) are never included in revenue or ROAS calculations -- they are projections, not confirmed cash.


Conversion Counts

Endpoint
GET /api/v1/conversions

Returns total conversions grouped by event type for the requested period.

Scope: conversions:read

Parameters

ParameterTypeRequiredDescription
date_fromstringYesStart date in YYYY-MM-DD format.
date_tostringYesEnd date in YYYY-MM-DD format.

Request

cURL
curl -H "Authorization: Bearer atb_live_YOUR_KEY" \
  "https://www.atribu.app/api/v1/conversions?date_from=2026-03-01&date_to=2026-03-25"
JavaScript
const res = await fetch(
  "https://www.atribu.app/api/v1/conversions?date_from=2026-03-01&date_to=2026-03-25",
  { headers: { Authorization: "Bearer atb_live_YOUR_KEY" } }
);
const { data } = await res.json();
Python
import requests

res = requests.get(
    "https://www.atribu.app/api/v1/conversions",
    headers={"Authorization": "Bearer atb_live_YOUR_KEY"},
    params={"date_from": "2026-03-01", "date_to": "2026-03-25"},
)
data = res.json()["data"]

Response

Success response (200 OK)
{
  "data": [
    { "event_type": "lead_created", "count": 85 },
    { "event_type": "appointment_booked", "count": 42 },
    { "event_type": "payment_received", "count": 28 },
    { "event_type": "closed_won", "count": 15 }
  ],
  "meta": {
    "date_from": "2026-03-01",
    "date_to": "2026-03-25",
    "profile_id": "uuid"
  }
}

Response fields

FieldTypeDescription
event_typestringConversion type (e.g., lead_created, payment_received).
countnumberTotal conversions of this type in the period.

Daily Conversions

Endpoint
GET /api/v1/conversions/timeseries

Returns daily conversion counts broken down by event type. Use this to chart conversion trends over time.

Scope: conversions:read

Parameters

ParameterTypeRequiredDescription
date_fromstringYesStart date in YYYY-MM-DD format.
date_tostringYesEnd date in YYYY-MM-DD format.

Request

cURL
curl -H "Authorization: Bearer atb_live_YOUR_KEY" \
  "https://www.atribu.app/api/v1/conversions/timeseries?date_from=2026-03-20&date_to=2026-03-25"
JavaScript
const res = await fetch(
  "https://www.atribu.app/api/v1/conversions/timeseries?date_from=2026-03-20&date_to=2026-03-25",
  { headers: { Authorization: "Bearer atb_live_YOUR_KEY" } }
);
const { data } = await res.json();
Python
import requests

res = requests.get(
    "https://www.atribu.app/api/v1/conversions/timeseries",
    headers={"Authorization": "Bearer atb_live_YOUR_KEY"},
    params={"date_from": "2026-03-20", "date_to": "2026-03-25"},
)
data = res.json()["data"]

Response

Success response (200 OK)
{
  "data": [
    { "date": "2026-03-20", "event_type": "lead_created", "count": 12 },
    { "date": "2026-03-20", "event_type": "payment_received", "count": 3 },
    { "date": "2026-03-21", "event_type": "lead_created", "count": 15 },
    { "date": "2026-03-21", "event_type": "appointment_booked", "count": 7 }
  ],
  "meta": {
    "date_from": "2026-03-20",
    "date_to": "2026-03-25",
    "profile_id": "uuid"
  }
}

Response fields

FieldTypeDescription
datestringCalendar date (YYYY-MM-DD).
event_typestringConversion type.
countnumberNumber of conversions of this type on this day.

Revenue by Day

Endpoint
GET /api/v1/revenue

Returns daily attributed revenue and ad spend. Revenue is computed using the selected attribution model and includes cash payments only.

Scope: conversions:read

Parameters

ParameterTypeRequiredDescription
date_fromstringYesStart date in YYYY-MM-DD format.
date_tostringYesEnd date in YYYY-MM-DD format.
modelstringNoAttribution model. Default last_touch. Options: first_touch, linear, time_decay, last_non_direct.

Request

cURL
curl -H "Authorization: Bearer atb_live_YOUR_KEY" \
  "https://www.atribu.app/api/v1/revenue?date_from=2026-03-20&date_to=2026-03-25&model=last_touch"
JavaScript
const res = await fetch(
  "https://www.atribu.app/api/v1/revenue?date_from=2026-03-20&date_to=2026-03-25&model=last_touch",
  { headers: { Authorization: "Bearer atb_live_YOUR_KEY" } }
);
const { data } = await res.json();
Python
import requests

res = requests.get(
    "https://www.atribu.app/api/v1/revenue",
    headers={"Authorization": "Bearer atb_live_YOUR_KEY"},
    params={
        "date_from": "2026-03-20",
        "date_to": "2026-03-25",
        "model": "last_touch",
    },
)
data = res.json()["data"]

Response

Success response (200 OK)
{
  "data": [
    { "date": "2026-03-20", "revenue": 2400.00, "spend": 680.00 },
    { "date": "2026-03-21", "revenue": 1800.00, "spend": 720.00 },
    { "date": "2026-03-22", "revenue": 3200.00, "spend": 650.00 }
  ],
  "meta": {
    "date_from": "2026-03-20",
    "date_to": "2026-03-25",
    "profile_id": "uuid"
  }
}

Response fields

FieldTypeDescription
datestringCalendar date (YYYY-MM-DD).
revenuenumberAttributed cash revenue in reporting currency.
spendnumberTotal ad spend across all platforms.

Cash Collected

Endpoint
GET /api/v1/revenue/cash

Returns total cash payments grouped by payment source and currency. Unlike the attributed revenue endpoint above, this shows raw payment totals regardless of attribution model.

Scope: conversions:read

Parameters

ParameterTypeRequiredDescription
date_fromstringYesStart date in YYYY-MM-DD format.
date_tostringYesEnd date in YYYY-MM-DD format.

Request

cURL
curl -H "Authorization: Bearer atb_live_YOUR_KEY" \
  "https://www.atribu.app/api/v1/revenue/cash?date_from=2026-03-01&date_to=2026-03-25"
JavaScript
const res = await fetch(
  "https://www.atribu.app/api/v1/revenue/cash?date_from=2026-03-01&date_to=2026-03-25",
  { headers: { Authorization: "Bearer atb_live_YOUR_KEY" } }
);
const { data } = await res.json();
Python
import requests

res = requests.get(
    "https://www.atribu.app/api/v1/revenue/cash",
    headers={"Authorization": "Bearer atb_live_YOUR_KEY"},
    params={"date_from": "2026-03-01", "date_to": "2026-03-25"},
)
data = res.json()["data"]

Response

Success response (200 OK)
{
  "data": [
    { "source": "stripe", "currency": "USD", "total_cash": "9800.00", "count": 35 },
    { "source": "mercadopago", "currency": "MXN", "total_cash": "45000.00", "count": 12 }
  ],
  "meta": {
    "date_from": "2026-03-01",
    "date_to": "2026-03-25",
    "profile_id": "uuid"
  }
}

Response fields

FieldTypeDescription
sourcestringPayment provider (stripe, mercadopago).
currencystringISO 4217 currency code.
total_cashstringExact decimal string. Sum of payments in the original currency (ADR 0018 — parse before doing arithmetic). Renamed from total.
countnumberNumber of payments.

Cash collected vs. attributed revenue

Cash collected (/revenue/cash) is the raw sum of payments, independent of any attribution model. Attributed revenue (/revenue) distributes payment value across the marketing touchpoints that contributed to the conversion, based on the selected model. Use cash collected for financial reconciliation and attributed revenue for marketing performance analysis.


Winning Campaign per Conversion

Endpoint
GET /api/v1/conversions/attribution

Every conversion of the window with the campaign that wins its credit, in one keyset-paginated walk. It replaces reading GET /api/v1/campaigns/{id}/conversions once per campaign and folding the rows: per campaign the credit is grouped exactly as that route groups it, and the winner is the campaign with the highest credited_weight; on a tie a direct credit beats an inherited one, then the most recent last_touch_time, then the lowest campaign_external_id. Only conversions credited to at least one campaign appear.

Scope: analytics:read or campaigns:read (either one). Unlike the other endpoints on this page it does not need conversions:read.

Parameters

ParameterTypeRequiredDescription
date_fromstringYesFirst day, YYYY-MM-DD, in the profile's timezone.
date_tostringYesLast day, inclusive. At most 366 days after date_from.
modelstringNoAttribution model. Defaults to last_touch.
limitintegerNoConversions per page, 1–1000. Defaults to 500. Anything else is a 400.
cursorstringNopagination.cursor from the previous page, unchanged.

Request

cURL
curl -H "Authorization: Bearer atb_live_YOUR_KEY" \
  "https://api.atribu.app/api/v1/conversions/attribution?date_from=2026-01-01&date_to=2026-10-15&limit=1000"
JavaScript
// Walk every page.
const rows = [];
let cursor;
do {
  const params = new URLSearchParams({ date_from: "2026-01-01", date_to: "2026-10-15", limit: "1000" });
  if (cursor) params.set("cursor", cursor);
  const res = await fetch(`https://api.atribu.app/api/v1/conversions/attribution?${params}`, {
    headers: { Authorization: "Bearer atb_live_YOUR_KEY" },
  });
  const page = await res.json();
  rows.push(...page.data);
  cursor = page.pagination.has_next ? page.pagination.cursor : undefined;
} while (cursor);
Python
import requests

rows, cursor = [], None
while True:
    params = {"date_from": "2026-01-01", "date_to": "2026-10-15", "limit": 1000}
    if cursor:
        params["cursor"] = cursor
    page = requests.get(
        "https://api.atribu.app/api/v1/conversions/attribution",
        headers={"Authorization": "Bearer atb_live_YOUR_KEY"},
        params=params,
    ).json()
    rows += page["data"]
    if not page["pagination"]["has_next"]:
        break
    cursor = page["pagination"]["cursor"]

Response

200 OK
{
  "data": [
    {
      "conversion_id": "00000000-0000-4000-8000-000000000001",
      "outcome_event_id": "00000000-0000-4000-8000-000000000002",
      "campaign_external_id": "120200000000000001",
      "campaign_name": "Implants — Leads",
      "credited_weight": 1,
      "credited_value": "50000",
      "touch_count": 3,
      "campaign_touch_count": 1,
      "path_length": 3,
      "event_time": "2026-09-30T14:03:11.123+00:00",
      "first_touch_time": "2026-09-21T22:10:05+00:00",
      "last_touch_time": "2026-09-21T22:10:05+00:00",
      "is_inherited": false,
      "event_type": "payment_received",
      "revenue_type": "cash",
      "value_amount": "50000",
      "currency": "CLP",
      "customer_key": "ghl_contact_0001",
      "is_first_payment": true
    }
  ],
  "pagination": { "has_next": true, "cursor": "eyJrIjoiY2EiLCJ0Ijo..." },
  "meta": {
    "model": "last_touch",
    "recomputed_at": "2026-10-15T12:00:00+00:00",
    "date_from": "2026-01-01",
    "date_to": "2026-10-15",
    "profile_id": "00000000-0000-4000-8000-0000000000aa"
  }
}

Response fields

FieldTypeDescription
conversion_idstringAtribu's conversion id.
outcome_event_idstring | nullThe outcome event id returned at ingest. outcome_event_id ?? conversion_id is a stable key.
campaign_external_idstringThe winning campaign's platform id.
campaign_namestringThe campaign's current name.
credited_weightnumberThe winning campaign's share of the conversion, 0–1.
credited_valuestring | nullIts credited share of the value. Exact decimal string.
touch_countintegerTouches in the whole path (path_length), else campaign_touch_count.
campaign_touch_countintegerCredited touches on the winning campaign.
path_lengthinteger | nullTouches in the whole path.
event_timestringWhen the conversion happened (ISO 8601).
first_touch_timestring | nullEarliest credited touch on the winning campaign.
last_touch_timestring | nullLatest credited touch on the winning campaign.
is_inheritedbooleanCredit came via a bridged lead→cash touch — never counted in ROAS.
event_typestringlead_created, appointment_booked, payment_received, closed_won, …
revenue_typestringcash, pipeline, …
value_amountstring | nullThe conversion's full value. Exact decimal string.
currencystring | nullReporting currency (ISO 4217).
customer_keystring | nullCRM contact id (not a name or email).
is_first_paymentboolean | nullThe customer's first payment. null unless revenue_type is cash.

Newest first. While pagination.has_next is true, pass pagination.cursor back as cursor with the same other parameters; a cursor this endpoint did not issue is a 400 invalid_parameter. There is no updated_since: an attribution recompute re-credits conversions without changing them, so re-walk the window (meta.recomputed_at says when the profile's attribution last changed).

On this page