Idempotency

Import and export job creation endpoints accept idempotency keys so network retries do not queue duplicate work. This guide explains when to use a key, how to choose one, and how to store the key with your own job record.

5 min read

Use Idempotency for Async Job Creation

Idempotency protects create-style requests from accidental duplication. In Mailrith's public API, idempotency is most important when queueing subscriber import and export jobs because those jobs can continue doing significant work after the request returns.

If your integration sends a job creation request and the network times out, your integration may not know whether Mailrith received the request. Repeating the request with the same Idempotency-Key lets Mailrith return the original job response instead of creating another job.

Use idempotency keys for import and export job creation whenever the caller, queue worker, or HTTP client might retry a request automatically.

  1. Create your own local job record before sending the Mailrith request.
  2. Generate one idempotency key for that local job.
  3. For an import, start a short-lived upload, ask the signed-in user to choose the CSV in Mailrith, then send the ready upload_id, mappings, and Idempotency-Key.
  4. For an export, send the Mailrith request with Idempotency-Key and the job payload.
  5. If the request succeeds, store the returned Mailrith job ID.
  6. If the request times out, retry with the same key and the exact same payload.
  7. If the CSV, mappings, or export filters change, create a new local job and a new idempotency key before sending another request.
  8. Monitor the job as described in Import and Export Jobs.
  • Generate one idempotency key for one logical job submission.
  • Reuse the same key only for retries of the exact same request body.
  • Store the key with your own job record so a queue worker can retry safely after a crash.
  • Use a new key when the upload, mappings, filters, or export selection changes.
  • Do not use one long-lived key for every import or export. A long-lived key would make different jobs look like the same request.
GET /v1/jobs/subscriber-exports

List subscriber export jobs

Returns a bounded cursor page of recent Subscriber export job summaries without loading selection payloads or generating download URLs.

View Schema
POST /v1/jobs/subscriber-exports

Create a subscriber export job

Queues an asynchronous Subscriber export for the authenticated workspace. Requires `subscribers:export` because the finished file contains bulk Subscriber data.

View Schema
GET /v1/jobs/subscriber-exports/{job_id}

Get a subscriber export job

Returns the current state of a previously created export job. Requires `subscribers:export` because completed jobs include a Subscriber CSV download URL.

View Schema
POST /v1/jobs/subscriber-import-uploads

Start a subscriber import upload

Creates a short-lived browser handoff where a signed-in user can choose a CSV file. Starting the handoff does not import Subscribers.

View Schema
GET /v1/jobs/subscriber-import-uploads/{upload_id}

Get a subscriber import upload

Returns upload readiness, bounded column metadata, and the row count without returning CSV contents.

View Schema
GET /v1/jobs/subscriber-imports

List subscriber import jobs

Returns a bounded cursor page of recent Subscriber import job summaries without loading stored CSV data or mapping payloads.

View Schema
POST /v1/jobs/subscriber-imports

Create a subscriber import job

Queues an asynchronous import job from a ready short-lived browser upload. CSV contents never pass through the agent request.

View Schema
GET /v1/jobs/subscriber-imports/{job_id}

Get a subscriber import job

Returns the current state of a previously created import job.

View Schema

Keep Retries Safe

A safe retry pattern starts before the HTTP request is sent. First create your own local job record, then generate an idempotency key, then send the Mailrith request with that key.

If the request succeeds, store Mailrith's returned job ID beside your own job record. If the request times out, retry with the same key and the same request body. If Mailrith already created the job, Mailrith can return the original response.

If you change the payload after a failed attempt, create a new idempotency key before retrying. A changed payload is no longer the same logical job.

  • Good key examples: import_2026_04_25_crm_001, a UUID, or your own queue job ID.
  • Bad key examples: import, retry, the workspace ID, or the API key.
  • Keep idempotency keys out of user-visible copy. They are operational identifiers, not labels.
  • Log the idempotency key with your job ID so you can trace repeated attempts.
Import Job With an Idempotency Key
# First start an upload and open data.upload_url for the signed-in user.
curl -X POST https://api.mailrith.com/v1/jobs/subscriber-import-uploads \
  -H "Authorization: Bearer mrk_example_secret_key"

# After GET /v1/jobs/subscriber-import-uploads/{upload_id} reports ready:
curl -X POST https://api.mailrith.com/v1/jobs/subscriber-imports \
  -H "Authorization: Bearer mrk_example_secret_key" \
  -H "Idempotency-Key: import-2026-04-11-website-signups" \
  -H "Content-Type: application/json" \
  -d '{
    "upload_id": "7a7283c5-c46e-4b70-bf57-b3385f8df983",
    "mappings": [
      {
        "csv_column": "Email",
        "field": { "type": "email" }
      },
      {
        "csv_column": "Name",
        "field": { "type": "name" }
      },
      {
        "csv_column": "Country",
        "field": { "type": "country" }
      },
      {
        "csv_column": "Subscriber Status",
        "field": { "type": "subscriber-status" }
      }
    ],
    "new_tag_name": "Website Signup"
  }'

Need Help Shipping an Integration?

Reach the Mailrith team if you need help planning a sync, validating a webhook flow, or troubleshooting a request.

Contact Mailrith

On this page

Jump to the section you need.