> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tracklysms.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Import Revenue History

> Import historical revenue records for previously sent messages.

Bulk import up to 1,000 historical revenue records per request. Each record is tied to an existing message and offer. Successfully imported records automatically update the associated ListContact's denormalized stats: `revenue_total`, `conversion_count`, and `last_revenue_at`.

## Authentication

<ParamField header="X-Api-Key" type="string" required>
  Your Trackly SMS API key. Format: `trk_[32-char-alphanumeric]`.
</ParamField>

## Body Parameters

<ParamField body="records" type="array" required>
  An array of revenue records to import. Maximum of 1,000 records per request. Each record accepts the following fields:

  <Expandable title="Record fields">
    <ParamField body="message_id" type="string" required>
      The message ID this revenue is associated with. The message must already exist in the system.
    </ParamField>

    <ParamField body="offer_id" type="string" required>
      The offer ID this revenue is associated with. The offer must already exist in the system.
    </ParamField>

    <ParamField body="revenue" type="float" required>
      The revenue amount to attribute.
    </ParamField>

    <ParamField body="payout" type="float" default="0">
      The payout amount associated with this revenue event.
    </ParamField>

    <ParamField body="attribution_type" type="string" default="migration">
      How the revenue was attributed. Defaults to `migration` for historical imports. Common values: `sale`, `click`, `send`, `migration`. This field accepts any string value for the history import endpoint.
    </ParamField>

    <ParamField body="timestamp" type="datetime">
      The revenue event timestamp in ISO 8601 format. Defaults to the current time if omitted.
    </ParamField>
  </Expandable>
</ParamField>

## Response Fields

<ResponseField name="success_count" type="integer">
  Number of revenue records successfully imported.
</ResponseField>

<ResponseField name="error_count" type="integer">
  Number of records that failed validation or processing.
</ResponseField>

<ResponseField name="total_revenue" type="float">
  Sum of all successfully imported revenue amounts.
</ResponseField>

<ResponseField name="errors" type="array">
  Array of error objects (maximum 100 returned). Each object contains:

  * `index` (integer) -- Position of the failed record in the input array.
  * `message_id` (string) -- The `message_id` of the failed record.
  * `code` (string) -- Machine-readable error code.
  * `error` (string) -- Human-readable error description.
</ResponseField>

<RequestExample>
  <CodeGroup>
    ```bash cURL theme={null}
    curl -X POST https://api.tracklysms.com/api/v2/history/revenue \
      -H "X-Api-Key: trk_your_api_key_here" \
      -H "Content-Type: application/json" \
      -d '{
        "records": [
          {
            "message_id": "a1b2c3d4",
            "offer_id": "offer_123",
            "revenue": 24.99,
            "payout": 5.00,
            "attribution_type": "sale",
            "timestamp": "2025-11-15T16:00:00Z"
          },
          {
            "message_id": "e5f6g7h8",
            "offer_id": "offer_456",
            "revenue": 12.50,
            "timestamp": "2025-11-15T17:30:00Z"
          },
          {
            "message_id": "i9j0k1l2",
            "offer_id": "offer_123",
            "revenue": 8.75,
            "attribution_type": "migration"
          }
        ]
      }'
    ```

    <Note>
      `message_id` is an opaque identifier — do not assume a fixed length or format. Store them as variable-length strings.
    </Note>

    ```python Python theme={null}
    import requests

    response = requests.post(
        "https://api.tracklysms.com/api/v2/history/revenue",
        headers={
            "X-Api-Key": "trk_your_api_key_here",
            "Content-Type": "application/json",
        },
        json={
            "records": [
                {
                    "message_id": "a1b2c3d4",
                    "offer_id": "offer_123",
                    "revenue": 24.99,
                    "payout": 5.00,
                    "attribution_type": "sale",
                    "timestamp": "2025-11-15T16:00:00Z",
                },
                {
                    "message_id": "e5f6g7h8",
                    "offer_id": "offer_456",
                    "revenue": 12.50,
                    "timestamp": "2025-11-15T17:30:00Z",
                },
                {
                    "message_id": "i9j0k1l2",
                    "offer_id": "offer_123",
                    "revenue": 8.75,
                    "attribution_type": "migration",
                },
            ],
        },
    )

    print(response.json())
    ```

    ```javascript Node.js theme={null}
    const response = await fetch("https://api.tracklysms.com/api/v2/history/revenue", {
      method: "POST",
      headers: {
        "X-Api-Key": "trk_your_api_key_here",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        records: [
          {
            message_id: "a1b2c3d4",
            offer_id: "offer_123",
            revenue: 24.99,
            payout: 5.0,
            attribution_type: "sale",
            timestamp: "2025-11-15T16:00:00Z",
          },
          {
            message_id: "e5f6g7h8",
            offer_id: "offer_456",
            revenue: 12.5,
            timestamp: "2025-11-15T17:30:00Z",
          },
          {
            message_id: "i9j0k1l2",
            offer_id: "offer_123",
            revenue: 8.75,
            attribution_type: "migration",
          },
        ],
      }),
    });

    const data = await response.json();
    console.log(data);
    ```
  </CodeGroup>
</RequestExample>

<ResponseExample>
  ```json 201 - Success theme={null}
  {
    "success_count": 3,
    "error_count": 0,
    "total_revenue": 46.24,
    "errors": []
  }
  ```

  ```json 201 - Partial Success theme={null}
  {
    "success_count": 2,
    "error_count": 1,
    "total_revenue": 37.49,
    "errors": [
      {
        "index": 2,
        "message_id": "i9j0k1l2",
        "code": "message_not_found",
        "error": "Message ID not found"
      }
    ]
  }
  ```

  ```json 400 - Validation Error theme={null}
  {
    "error": "records array is required",
    "code": "missing_records"
  }
  ```

  ```json 413 - Payload Too Large theme={null}
  {
    "error": "Maximum 1000 records per request",
    "code": "too_many_records"
  }
  ```
</ResponseExample>

## Error Codes

Only two conditions reject the whole request. Every other error is reported **per record**: the request still returns `201`, and each failed record appears in the response's `errors[]` array with a `code` (see the Partial Success example above).

### Request errors (HTTP status)

| HTTP Status | Error Code         | Description                                                    |
| ----------- | ------------------ | -------------------------------------------------------------- |
| 400         | `missing_records`  | The `records` field is required and must be a non-empty array. |
| 413         | `too_many_records` | Exceeded the maximum of 1,000 records per request.             |

Authenticated requests can also fail with `401 invalid_credentials`, `403 account_suspended`, or `429 rate_limited` — see [Error Codes](/api-reference/v2/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_offer_id`   | A record is missing the `offer_id` field.                   |
| `missing_revenue`    | A record is missing the `revenue` field.                    |
| `message_not_found`  | No message exists with the given `message_id`.              |
| `offer_not_found`    | No offer exists with the given `offer_id`.                  |
| `contact_not_found`  | The contact associated with the message could not be found. |
| `save_error`         | An unexpected error occurred while saving the record.       |

## Next Steps

<CardGroup cols={2}>
  <Card title="Reporting Overview" icon="chart-bar" href="/guides/reporting/overview">
    Analyze revenue data
  </Card>

  <Card title="Bulk Create Contacts" icon="users" href="/api-reference/v2/contacts/bulk-create">
    Import contacts alongside history
  </Card>
</CardGroup>
