Skip to content

Sales, with first and last touch side by side, and cost per lead, appointment and sale

GET
/v1/reports/sales
curl --request GET \
--url 'https://api.threadkite.app/v1/reports/sales?preset=today&granularity=day&inboxType=sales&sourceCategory=website&channel=email&trafficChannel=paid_search&touch=first' \
--header 'Authorization: Bearer <token>'

A sale is a thread closed as Won, a deal link marked won, or one your systems reported (POST /v1/sales), counted once per thread per deal. Each is credited to the contact’s leads in the look-back (first touch: the earliest; last touch: the latest before the sale). Only sales in threads you can see. Part of Pro.

preset

Date range in your organisation’s time zone. Defaults to 30d (the last 30 days, today included); custom needs from and to. Dates without a preset are custom.

string
Allowed values: today 7d 30d this_month last_month custom

Date range in your organisation’s time zone. Defaults to 30d (the last 30 days, today included); custom needs from and to. Dates without a preset are custom.

from

First day (custom ranges), YYYY-MM-DD

string format: date

First day (custom ranges), YYYY-MM-DD

to

Last day, included (custom ranges)

string format: date

Last day, included (custom ranges)

granularity

Group charts over time by day or week. Defaults to days up to 62 days, else weeks

string
Allowed values: day week

Group charts over time by day or week. Defaults to days up to 62 days, else weeks

inboxId

Only this inbox (one you can see)

string format: uuid

Only this inbox (one you can see)

inboxType

Only inboxes of this type

string
Allowed values: sales service parts bodyshop general

Only inboxes of this type

locationId

Only leads at this location

string format: uuid

Only leads at this location

sourceId

Only leads from this source

string format: uuid

Only leads from this source

sourceCategory

Only sources of this kind

string
Allowed values: website marketplace website_provider dms social phone email other

Only sources of this kind

ownerUserId

Only threads this person owns, or unassigned for threads nobody owns

string
>= 1 characters <= 100 characters

Only threads this person owns, or unassigned for threads nobody owns

channel

Only leads on this channel

string
Allowed values: email sms whatsapp chat phone form api marketplace visit messenger instagram internal

Only leads on this channel

campaignId

Only leads from this campaign

string format: uuid

Only leads from this campaign

trafficChannel

Only leads whose visit came this way (paid search, email…)

string
Allowed values: paid_search paid_social display email sms affiliate organic_search organic_social referral direct other

Only leads whose visit came this way (paid search, email…)

touch

Sales, funnel and campaigns reports: credit each sale to the first lead in the look-back (first) or the latest before the sale (last, the default). Source, source type, channel, campaign and traffic channel filters then apply to that lead.

string
Allowed values: first last

Sales, funnel and campaigns reports: credit each sale to the first lead in the look-back (first) or the latest before the sale (last, the default). Source, source type, channel, campaign and traffic channel filters then apply to that lead.

Media typeapplication/json
object
range
required
object
from
required
string format: date
to
required
string format: date
days
required
number
preset
required
string
Allowed values: today 7d 30d this_month last_month custom
timeZone
required

Your organisation’s time zone

string
granularity
required
string
Allowed values: day week
previous
required

The period compared against

object
from
required
string format: date
to
required
string format: date
days
required
number
limited
required

The Free plan’s basic reports: only leads by source and first response times are filled in; other breakdowns are empty

boolean
touch
required

Which lead each sale is credited to

string
Allowed values: first last
lookbackDays
required

Days before a sale that leads count towards it

number
currency
required
string
costsShown
required

Whether cost per lead, appointment and sale are worked out. Spend is entered for the whole organisation (or a location), so costs are only shown without inbox, owner or channel filters, to people who see every inbox.

boolean
overall
required
object
sales
required

Sales in the range (each deal once)

number
previousSales
required

Sales in the period before

number
valueMinor
required

Value of sales with a value in the report’s currency, minor units

number
previousValueMinor
required
number
attributed
required

Sales with at least one lead from the contact in the look-back

number
itemMatches
required

Sales where a lead named the item sold

number
leads
required

Leads in the range (same filters)

number
conversionRate
required

Sales as a share of leads (0 to 1)

number
nullable
spend
required

All spend in the range

object
spendMinor
required

Spend in the range; null when no month in it has spend entered, in minor units of the report’s currency (pence for GBP)

number
nullable
spendComplete
required

Every month the range touches has spend entered

boolean
missingMonths
required

Months in the range with no spend entered (YYYY-MM)

Array<string>
costPerSale
required

All spend divided by all sales, in minor units of the report’s currency (pence for GBP)

number
nullable
series
required
Array<object>
object
date
required

The day, or the Monday of the week

string format: date
sales
required
number
valueMinor
required
number
bySource
required

First and last touch side by side; most sales first

Array<object>
object
spendMinor
required

Spend in the range; null when no month in it has spend entered, in minor units of the report’s currency (pence for GBP)

number
nullable
spendComplete
required

Every month the range touches has spend entered

boolean
missingMonths
required

Months in the range with no spend entered (YYYY-MM)

Array<string>
sourceId
required

Null for leads with no source

string format: uuid
nullable
name
required
string
category
required
string
nullable
Allowed values: website marketplace website_provider dms social phone email other
logoUrl
required

Logo address; null shows an initial badge

string
nullable
leads
required

Leads from this source in the range

number
appointments
required

Appointments booked after a lead from this source

number
firstTouchSales
required

Sales whose first touch was this source

number
lastTouchSales
required

Sales whose last touch was this source

number
firstTouchValueMinor
required
number
lastTouchValueMinor
required
number
costPerLead
required

Spend per lead; null without spend or leads, or when costs are not shown, in minor units of the report’s currency (pence for GBP)

number
nullable
costPerAppointment
required

Spend per appointment booked, in minor units of the report’s currency (pence for GBP)

number
nullable
costPerSale
required

Spend per sale, by the touch the report uses, in minor units of the report’s currency (pence for GBP)

number
nullable
byOrigin
required
Array<object>
object
origin
required
string
Allowed values: outcome link api
label
required
string
sales
required
number
byPerson
required

By the person credited

Array<object>
object
key
required

The id grouped by; null for none

string
nullable
name
required
string
sales
required
number
valueMinor
required
number
byLocation
required
Array<object>
object
key
required

The id grouped by; null for none

string
nullable
name
required
string
sales
required
number
valueMinor
required
number
byInbox
required
Array<object>
object
key
required

The id grouped by; null for none

string
nullable
name
required
string
sales
required
number
valueMinor
required
number
byCompany
required

Sales by the contact’s primary company when the report is worked out (so a contact’s history moves with them), the 20 companies with most first, then “No company”

Array<object>
object
key
required

The id grouped by; null for none

string
nullable
name
required
string
sales
required
number
valueMinor
required
number
recent
required

The latest 50 sales

Array<object>
object
saleId
required
string format: uuid
soldAt
required
string format: date-time
threadId
required
string format: uuid
nullable
contactName
required

The contact’s name, if known

string
nullable
item
required
One of:
object
id
required
string format: uuid
title
required
string
valueMinor
required

Minor units of currency

number
nullable
currency
required
string
nullable
origin
required
string
Allowed values: outcome link api
firstTouch
required
One of:
object
sourceId
required
string format: uuid
nullable
name
required
string
leadAt
required
string format: date-time
lastTouch
required
One of:
object
sourceId
required
string format: uuid
nullable
name
required
string
leadAt
required
string format: date-time
itemMatch
required

A lead in the look-back named the item sold

boolean

Example

{
"range": {
"from": "2026-09-06",
"to": "2026-10-05",
"days": 30,
"preset": "today",
"timeZone": "Europe/London",
"granularity": "day",
"previous": {
"from": "2026-09-06",
"to": "2026-10-05",
"days": 30
}
},
"touch": "first",
"lookbackDays": 90,
"currency": "GBP",
"spend": {
"missingMonths": [
"2026-09"
]
},
"bySource": [
{
"missingMonths": [
"2026-09"
],
"name": "Website form",
"category": "website"
}
],
"byOrigin": [
{
"origin": "outcome",
"label": "Closed as Won"
}
],
"byPerson": [
{
"name": "Leeds"
}
],
"byLocation": [
{
"name": "Leeds"
}
],
"byInbox": [
{
"name": "Leeds"
}
],
"byCompany": [
{
"name": "Leeds"
}
],
"recent": [
{
"item": {
"title": "Blue cotton shirt"
},
"currency": "GBP",
"origin": "outcome",
"firstTouch": {
"name": "Google Ads"
},
"lastTouch": {
"name": "Google Ads"
}
}
]
}

Unusable dates or filters

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
errors

With code invalid: every problem with the request, by field (message is the first)

Array<object>
object
path
required

Dotted path inside the item, e.g. attributes.price.amountMinor

string
message
required
string

Example

{
"statusCode": 403,
"code": "forbidden",
"message": "You do not have permission to do that.",
"errors": [
{
"message": "Enter the make"
}
]
}

Missing or invalid API key

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
errors

With code invalid: every problem with the request, by field (message is the first)

Array<object>
object
path
required

Dotted path inside the item, e.g. attributes.price.amountMinor

string
message
required
string

Example

{
"statusCode": 403,
"code": "forbidden",
"message": "You do not have permission to do that.",
"errors": [
{
"message": "Enter the make"
}
]
}

The organisation is on the Free plan (code upgrade_required)

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
feature
required

The feature that needs Pro

string
nullable
Allowed values: extraSeats connections aiAssistant aiReplies rules fullReports api inboxes bulkMessages videoMessages
upgradePath
required

Where to upgrade or pay, in the web app

string

Example

{
"statusCode": 402,
"code": "upgrade_required",
"message": "Connections are part of Pro. Upgrade to connect texts, calls and more.",
"feature": "extraSeats",
"upgradePath": "/settings/billing"
}

The key does not have the scope (or the person the permission)

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
errors

With code invalid: every problem with the request, by field (message is the first)

Array<object>
object
path
required

Dotted path inside the item, e.g. attributes.price.amountMinor

string
message
required
string

Example

{
"statusCode": 403,
"code": "forbidden",
"message": "You do not have permission to do that.",
"errors": [
{
"message": "Enter the make"
}
]
}

Rate limit reached

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
retryAfter
required

Seconds until another request will be accepted

number

Example

{
"statusCode": 429,
"code": "rate_limited",
"message": "Too many requests. Each key can make 120 requests a minute; try again in 12 seconds.",
"retryAfter": 12
}