{
"success_count": 2,
"error_count": 0,
"total_revenue": 37.49,
"errors": []
}
{
"success_count": 1,
"error_count": 1,
"total_revenue": 24.99,
"errors": [
{
"index": 1,
"code": "message_not_found",
"error": "Message ID not found"
}
]
}
{
"code": "missing_records",
"error": "records array is required"
}
{
"code": "too_many_records",
"error": "Maximum 1000 records per request"
}
Revenue (v2)
Bulk Record Revenue
Record multiple revenue attributions in a single request.
POST
/
v2
/
revenue
/
bulk
{
"success_count": 2,
"error_count": 0,
"total_revenue": 37.49,
"errors": []
}
{
"success_count": 1,
"error_count": 1,
"total_revenue": 24.99,
"errors": [
{
"index": 1,
"code": "message_not_found",
"error": "Message ID not found"
}
]
}
{
"code": "missing_records",
"error": "records array is required"
}
{
"code": "too_many_records",
"error": "Maximum 1000 records per request"
}
Record up to 1,000 revenue events in a single API call. Each record follows the same schema as the single revenue endpoint. The response includes per-record error details so you can identify and retry failures individually.
Authenticated requests can also fail with Per-record errors (
Authentication
string
required
Your Trackly SMS API key, formatted as
trk_ followed by 32 lowercase alphanumeric characters.Body Parameters
array
required
An array of revenue records to process. Maximum of 1,000 records per request. Each record accepts the following fields:
Show Record fields
Show Record fields
string
required
An opaque, variable-length message identifier. This is the same value passed via the
{{sendId}} macro. Do not assume a fixed length or format.float
required
Revenue amount. Must be greater than or equal to
0.string
required
One of:
sale, click, or send.string
The offer ID this revenue is associated with. Can be either the
externalId or the offer ID generated by TracklySMS. If omitted, the system will attempt to resolve the offer from the message’s short link.datetime
ISO 8601 timestamp. Assumed to be UTC if no timezone is provided. Defaults to the current time.
boolean
Accepted for forward compatibility but not currently used — the value is ignored and has no effect on how records are stored or attributed.
Response Fields
integer
Number of revenue records successfully created.
integer
Number of records that failed validation or processing.
float
Sum of all successfully processed revenue amounts.
array
Array of error objects for failed records. Each object contains:
index(integer) — Position of the failed record in the input array.code(string) — Machine-readable error code.error(string) — Human-readable error description.
cURL
curl -X POST https://api.tracklysms.com/api/v2/revenue/bulk \
-H "X-Api-Key: trk_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"message_id": "a1b2c3d4",
"revenue": 24.99,
"offer_id": "offer_123",
"attribution_type": "sale"
},
{
"message_id": "e5f6g7h8",
"revenue": 12.50,
"offer_id": "offer_456",
"attribution_type": "click"
}
],
"imported": false
}'
message_id is an opaque identifier — do not assume a fixed length or format. Store them as variable-length strings.Python
import requests
response = requests.post(
"https://api.tracklysms.com/api/v2/revenue/bulk",
headers={
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
json={
"records": [
{
"message_id": "a1b2c3d4",
"revenue": 24.99,
"offer_id": "offer_123",
"attribution_type": "sale",
},
{
"message_id": "e5f6g7h8",
"revenue": 12.50,
"offer_id": "offer_456",
"attribution_type": "click",
},
],
"imported": False,
},
)
print(response.json())
Node.js
const response = await fetch("https://api.tracklysms.com/api/v2/revenue/bulk", {
method: "POST",
headers: {
"X-Api-Key": "trk_your_api_key_here",
"Content-Type": "application/json",
},
body: JSON.stringify({
records: [
{
message_id: "a1b2c3d4",
revenue: 24.99,
offer_id: "offer_123",
attribution_type: "sale",
},
{
message_id: "e5f6g7h8",
revenue: 12.5,
offer_id: "offer_456",
attribution_type: "click",
},
],
imported: false,
}),
});
const data = await response.json();
console.log(data);
{
"success_count": 2,
"error_count": 0,
"total_revenue": 37.49,
"errors": []
}
{
"success_count": 1,
"error_count": 1,
"total_revenue": 24.99,
"errors": [
{
"index": 1,
"code": "message_not_found",
"error": "Message ID not found"
}
]
}
{
"code": "missing_records",
"error": "records array is required"
}
{
"code": "too_many_records",
"error": "Maximum 1000 records per request"
}
Error Codes
Only three validation conditions reject the whole request up front. Every other validation error is reported per record: the request still returns201, and each failed record appears in the response’s errors[] array with a code (see the Partial Success example above). If saving the validated records fails, the request fails with a 500 error instead of returning a partial-success body.
Request errors (HTTP status)
| HTTP Status | Error Code | Description |
|---|---|---|
| 400 | missing_records | The records field is required and must be a non-empty array. |
| 400 | invalid_body | The request body must be a JSON object. |
| 413 | too_many_records | Exceeded the maximum of 1,000 records per request. |
401 invalid_credentials or 403 account_suspended — see Error Codes.
Per-record errors (errors[].code, returned with HTTP 201)
| Error Code | Description |
|---|---|
missing_message_id | A record is missing the message_id field. |
missing_revenue | A record is missing the revenue field. |
missing_attribution_type | A record is missing the attribution_type field. |
invalid_message_id | A record’s message_id is not a string. |
invalid_record | A record is not a JSON object. |
invalid_revenue | Revenue must be a number greater than or equal to 0. |
invalid_attribution_type | Must be one of: sale, click, or send. |
message_not_found | No message with the given ID exists in your account (also returned when the message belongs to a different account). |
Next Steps
Revenue Tracking
Track and attribute revenue
Send Message
Send messages to drive revenue