Release Candidate — the API is undergoing final validation before general availability, and minor changes may still occur.
Hellomateo API Documentation
Recipes

Contact and entity batch imports

Submit contact, deal, and appointment imports and poll their status.

Use the entity-specific batch import endpoints when you need to submit multiple contacts, deals, or appointments for asynchronous processing.

EntityCreate a batchPoll one batchList batches
ContactsPOST /v1/contacts/imports/batchGET /v1/contacts/imports/{id}GET /v1/contacts/imports
DealsPOST /v1/deals/imports/batchGET /v1/deals/imports/{id}GET /v1/deals/imports
AppointmentsPOST /v1/appointments/imports/batchGET /v1/appointments/imports/{id}GET /v1/appointments/imports

Every batch request creates a new import job. The API does not deduplicate requests by payload or external_id, so retrying a successful request creates another job.

Submit an inline payload

Batch requests use JSON and include a source discriminated by source.type. An inline source contains a non-empty records array; a file source contains a canonical FileInput under file. Each inline record is an entity import record. The request body limit is 5 MB. Multipart uploads are not supported by these endpoints.

curl https://api.getmateo.com/v1/contacts/imports/batch \
  --request POST \
  --header "Authorization: Bearer $MATEO_API_TOKEN" \
  --header "hellomateo-version: 2026-07-01" \
  --header "Content-Type: application/json" \
  --data '{
    "external_id": "erp-contact-import-2026-07-14",
    "contact_list_external_id": "newsletter",
    "source": {
      "type": "inline",
      "records": [
        {
          "external_id": "customer-1001",
          "full_name": "Ada Lovelace",
          "identities": [
            {
              "channel_type": "email",
              "type": "email_address",
              "value": "ada@example.com"
            }
          ]
        },
        {
          "external_id": "customer-1002",
          "full_name": "Grace Hopper",
          "identities": [
            {
              "channel_type": "sms",
              "type": "phone_number",
              "value": {
                "candidates": ["079 123 45 67", "+41791234567"],
                "country_codes": ["CH"]
              }
            }
          ]
        }
      ]
    }
  }'

For contact batches, use at most one of contact_list_id and contact_list_external_id. Contact handles belong in identities. SMS and WhatsApp identity values accept either one string or an object containing ordered candidates and optional country_codes. Country codes must be valid ISO 3166-1 alpha-2 codes and are tried in order. An invalid phone produces a nonfatal import hint while the remaining contact fields are still imported.

Submit an uploaded file

For larger imports, request a signed upload URL, upload a CSV or JSON file with a raw PUT, and submit the returned File ID as source.file for any entity batch endpoint. Preserve Content-Type: text/csv or Content-Type: application/json on the upload because the import worker uses the provider metadata to select the parser.

UPLOAD_RESPONSE=$(curl https://api.getmateo.com/v1/files/upload_url \
  --request POST \
  --header "Authorization: Bearer $MATEO_API_TOKEN" \
  --header "hellomateo-version: 2026-07-01" \
  --header "Content-Type: application/json" \
  --data '{"purpose":"import_payload","filename":"deals.csv"}')

UPLOAD_URL=$(echo "$UPLOAD_RESPONSE" | jq -r '.url')
FILE_ID=$(echo "$UPLOAD_RESPONSE" | jq -r '.file_id')

curl "$UPLOAD_URL" \
  --request PUT \
  --header "Content-Type: text/csv" \
  --data-binary @deals.csv

curl https://api.getmateo.com/v1/deals/imports/batch \
  --request POST \
  --header "Authorization: Bearer $MATEO_API_TOKEN" \
  --header "hellomateo-version: 2026-07-01" \
  --header "Content-Type: application/json" \
  --data "{\"source\":{\"type\":\"file\",\"file\":{\"file_id\":\"$FILE_ID\"}},\"allow_reassignment\":false}"

The same { "source": { "type": "file", "file": { "file_id": "..." } } } input works for contact, deal, and appointment batch imports. The discriminator ensures that file sources cannot include inline records. The batch endpoint copies the uploaded source into the import's storage path. Both the uploaded source and the import-owned copy keep the immutable import_payload purpose.

Deal and appointment batches use the same envelope and accept their respective record fields:

{
  "external_id": "erp-deal-import-2026-07-14",
  "allow_reassignment": false,
  "source": {
    "type": "inline",
    "records": [
      {
        "external_id": "deal-1001",
        "name": "Annual renewal",
        "contact_external_id": "customer-1001"
      }
    ]
  }
}

Send that body to POST /v1/deals/imports/batch. For appointments, send the appointment records to POST /v1/appointments/imports/batch. Both endpoints use allow_reassignment; the route determines which entity is imported.

Batch routes return 415 Unsupported Media Type when Content-Type is missing or is not a JSON media type. They return 413 Payload Too Large when the JSON body exceeds 5 MB. Malformed JSON and payload validation failures return 400 Bad Request.

A successful submission returns 201 Created and the new import job. This abbreviated example highlights the fields used for polling:

{
  "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "external_id": "erp-contact-import-2026-07-14",
  "status": "pending",
  "started_at": null,
  "completed_at": null,
  "total_count": null,
  "success_count": null,
  "failed_count": null,
  "failure": null
}

Poll the import

Build the detail URL from the returned id and the same entity collection used to create the job:

curl https://api.getmateo.com/v1/contacts/imports/3c90c3cc-0d44-4b50-8888-8dd25736052a \
  --header "Authorization: Bearer $MATEO_API_TOKEN" \
  --header "hellomateo-version: 2026-07-01"

Batch 1 exposes coarse status semantics:

  • pending means at least one result count is not available yet.
  • completed means total_count, success_count, and failed_count are all available.

A completed job may still contain failed records. Check success_count and failed_count rather than treating completed as “every record succeeded.” The status schema also reserves processing, failed, and unknown for the persisted lifecycle rollout. The following response is abbreviated:

{
  "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
  "status": "completed",
  "total_count": 2,
  "success_count": 1,
  "failed_count": 1,
  "failure": null
}

Poll with backoff until the job reaches a terminal status. Do not repeatedly submit the batch to check whether it finished.

List and filter imports

Entity import history is separate for contacts, deals, and appointments. Lists are ordered by creation time and use cursor pagination.

curl --globoff 'https://api.getmateo.com/v1/contacts/imports?status[in]=pending,completed&limit=20' \
  --header "Authorization: Bearer $MATEO_API_TOKEN" \
  --header "hellomateo-version: 2026-07-01"

Follow next_page_url when it is present. You can also filter by one external_id or use external_id[in] for multiple values.

On this page