# Exports API reference

Start answers or leads CSV exports, inspect asynchronous job status, and retrieve download metadata.

Available on the Scale and Pro plans.

Exports create CSV files from recorded visitor data. Starting an export queues work; a 202 response does not mean a file is ready. Exports do not edit or publish the project draft.

## Authentication and operations

Paths below follow `https://api.rightmessage.com/v1/{teamPid}`. Starting an export requires `write`; status and download GET requests accept `read` or `write`. The `*` ability satisfies both. The [shared request conventions](https://rightmessage.com/help/rest-api-overview#base-url-and-authentication) cover bearer authentication, headers, project access, and rate limits.

Starting either export also requires CSV export access. Status and download routes check the job's project ownership but do not repeat that export-creation entitlement check.

| Method and path                 | Purpose                               | Success |
| ------------------------------- | ------------------------------------- | ------- |
| POST `/exports/answers`         | Export free-text responses            | 202     |
| POST `/exports/leads`           | Export the leads feed                 | 202     |
| GET `/exports/{jobId}`          | Read the cached job snapshot          | 200     |
| GET `/exports/{jobId}/download` | Read download metadata, not CSV bytes | 200     |

## POST /exports/answers

Starts a CSV export of free-text Other answers.

Optional body field `segment_id` is a string. Omit it for all available free-text responses. There are no start/end parameters on this operation. This is an export of free-text Other answers, not a matrix of every choice answer.

Question and segment names come from stable configuration. CSV columns are `id`, `timestamp`, `question`, `content`, `email`, and `segments`. Timestamps use `Y-m-d H:i:s` when parseable. The export job has a 30-minute timeout.

Illustrative request and response, not executed:

```http
POST /v1/123456/exports/answers HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
Content-Type: application/json

{"segment_id":"seg_example1"}
```

```json
{"data":{"job_id":"export_42_example","status":"processing","message":"Export started."}}
```

## POST /exports/leads

Starts a CSV export of lead-conversion records in the requested period.

| Body field   | Validation and default                                                  |
| ------------ | ----------------------------------------------------------------------- |
| `start`      | Optional date; defaults to the current time minus 30 days               |
| `end`        | Optional date; defaults to the current time                             |
| `segment_id` | Optional string accepted by validation. It does not filter this export. |

The endpoint does not enforce date ordering or a maximum range. Dates are parsed into instants, with inclusive start and end bounds. A date-only `end` is midnight at the start of that date, not the end of the day. Supply explicit timestamps when the boundary matters. The export job has a 30-minute timeout.

The export currently stops after at most 100 newest matching lead records because pagination does not continue past that first result set. It can report `completed` even when more records matched. Do not treat its completion counts as proof of a complete export.

The CSV writes `Email` first, followed by the collected export fields alphabetically. Saved conversion answers take precedence over fallback segment membership. Fields and values use stable synchronization mappings where available, with group names and segment names as fallbacks. Additional stored custom fields are included without overwriting an already-produced column. The output is lead-conversion records, not a deduplicated contact list.

Illustrative request and response, not executed:

```http
POST /v1/123456/exports/leads HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
Content-Type: application/json

{"start":"2026-10-01","end":"2026-10-13"}
```

```json
{"data":{"job_id":"export_leads_42_example","status":"processing","message":"Export started."}}
```

## GET /exports/{jobId}

Returns the latest status for an export job.

`jobId` is the opaque string returned at start. No other parameters are accepted. The job must belong to the project in the URL.

The `data` object is a snapshot, not a fixed progress schema. It always contains `status`, `message`, and numeric `team_id`. The initial snapshot has `created_at`, `total: 0`, and `processed: 0`. Later snapshots use `updated_at` instead. Counts appear only when an update supplies them. They can be absent from intermediate or failed snapshots.

States are `processing`, `completed`, and `failed`. Completed snapshots add `download_url`, `filename`, and `expires_at`. A failed snapshot remains HTTP 200 with `status: "failed"` and a failure message.

Illustrative request and completed response, not executed:

```http
GET /v1/123456/exports/export_leads_42_example HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
```

```json
{"data":{"team_id":42,"status":"completed","message":"Export completed successfully","updated_at":"2026-10-13T12:01:00+00:00","total":20,"processed":20,"download_url":"https://example.com/exports/workforge.csv","filename":"leads-export-2026-10-13-120000.csv","expires_at":"2026-10-14T12:01:00+00:00"}}
```

Missing, expired, or wrong-project job snapshot returns 404:

```json
{"data":{"status":"not_found","message":"Export job not found. It may have expired."}}
```

## GET /exports/{jobId}/download

Returns JSON download metadata, not a redirect and not the file body. Success contains `data.download_url`, nullable `data.filename`, and nullable `data.expires_at`.

Illustrative request and response, not executed:

```http
GET /v1/123456/exports/export_leads_42_example/download HTTP/1.1
Host: api.rightmessage.com
Authorization: Bearer YOUR_API_TOKEN
Accept: application/json
```

```json
{"data":{"download_url":"https://example.com/exports/workforge.csv","filename":"leads-export-2026-10-13-120000.csv","expires_at":"2026-10-14T12:01:00+00:00"}}
```

| Status | Exact endpoint-specific body                                                                               |
| ------ | ---------------------------------------------------------------------------------------------------------- |
| 404    | `{"error":"Export job not found. It may have expired."}`                                                   |
| 409    | `{"error":"Export is not yet complete.","status":"processing"}`; status reflects the job, including failed |
| 500    | `{"error":"Download URL not available."}` when a completed job has no URL                                  |

## Retention, privacy, and common errors

Each worker status update replaces the cache snapshot and sets its lifetime to 24 hours. The completion metadata reports an expiry 24 hours later. The jobs store the CSV with public visibility and return a storage URL; they do not create an authenticated v1 download stream or a signed URL here. The expiry metadata is not proof that the file becomes inaccessible at that instant. Treat the URL as sensitive and do not place it in public logs.

The shared errors are 401 for authentication, 403 for permissions or export entitlement, 404 for an unknown project, 422 for invalid body fields, 429 for throttling, and 503 for maintenance. There is no cancellation or export-deletion operation in this v1 family.
