eusend-dev Contacts API

The people in an audience, and their subscription state.

Operations 7

POST /audiences/{id}/contacts Add or upsert a contact #
GET /audiences/{id}/contacts List contacts in an audience #
POST /audiences/{id}/contacts/batch Upsert up to 1,000 contacts #
POST /audiences/{id}/contacts/batch-delete Delete up to 1,000 contacts by id #
GET /audiences/{id}/contacts/{contactId} Get a contact #
PATCH /audiences/{id}/contacts/{contactId} Update a contact #
DELETE /audiences/{id}/contacts/{contactId} Remove a contact #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/eusend-dev:eusend-dev-contacts-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

eusend-dev-contacts-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: eusend Contacts API
  version: 1.0.0
  summary: EU-native transactional and marketing email API.
  description: 'eusend sends transactional and marketing email from infrastructure that stays inside the European Union — processing, storage and delivery alike.


    **When to use this API.** Reach for eusend to send application email from server-side code: password resets, receipts, verification codes, notifications, and opt-in marketing broadcasts to a list you own. It is a good fit when GDPR or EU data residency is a requirement, when you are migrating off Resend, SendGrid, Mailgun, Postmark or SES, or when you need SMTP relay rather than an HTTP call. It is not a fit for inbox hosting, for reading a user’s mailbox, or for sending to purchased or scraped lists.


    **Getting started.** Create an account, verify a sending domain by publishing the DNS records the domain endpoints hand back, then `POST /emails`. A key prefixed `eu_test_` exercises the whole pipeline without delivering anything.


    **Authentication.** Every authenticated endpoint takes `Authorization: Bearer `. Keys are `eu_live_…` or `eu_test_…`, and carry either `full_access` or the send-only `sending_access` permission.


    **Errors.** Non-2xx responses are always `{ "error": string, "code": string }`. Branch on `code`; the list is enumerated on the `Error` schema.


    **Conventions.** Request bodies use snake_case and reject unknown properties. Response bodies use camelCase, except the pagination cursor and a handful of endpoints noted inline. List endpoints are cursor-paginated, newest first. Every response carries `X-Request-Id`.'
  termsOfService: https://eusend.dev/legal/terms
  contact:
    name: eusend support
    email: support@eusend.dev
    url: https://eusend.dev/support
  license:
    name: Proprietary
    url: https://eusend.dev/legal/terms
servers:
- url: https://api.eusend.dev
  description: Production
security:
- bearerAuth: []
tags:
- name: Contacts
  description: The people in an audience, and their subscription state.
paths:
  /audiences/{id}/contacts:
    post:
      operationId: createContact
      summary: Add or upsert a contact
      description: Addresses are lowercased. Adding an address already in the audience updates it rather than failing.
      tags:
      - Contacts
      parameters:
      - name: id
        in: path
        required: true
        description: The audience id.
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateContactRequest'
      responses:
        '201':
          description: Created or updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          description: The request body or query failed validation (`VALIDATION_ERROR`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing, malformed, or revoked API key (`UNAUTHORIZED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The key exists but may not reach this resource — a `sending_access` key outside the send paths, a test-mode key writing live state, or an account on hold (`FORBIDDEN`, `ACCOUNT_RESTRICTED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such resource on this organization (`NOT_FOUND`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded (`RATE_LIMITED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Unexpected server error (`INTERNAL_ERROR`). Carries `request_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    get:
      operationId: listContacts
      summary: List contacts in an audience
      description: Newest first, cursor-paginated. Use `nextCursor` from the response as the next `cursor`.
      tags:
      - Contacts
      parameters:
      - name: id
        in: path
        required: true
        description: The audience id.
        schema:
          type: string
          format: uuid
      - name: limit
        in: query
        schema:
          type: integer
          minimum: 1
          maximum: 1000
          default: 50
      - name: cursor
        in: query
        schema:
          type: string
          format: uuid
      - name: search
        in: query
        description: Substring match on the email address.
        schema:
          type: string
      - name: subscribed
        in: query
        description: true for subscribed only, false for unsubscribed only.
        schema:
          type: boolean
      responses:
        '200':
          description: A page of contacts.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListContactsResponse'
        '401':
          description: Missing, malformed, or revoked API key (`UNAUTHORIZED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The key exists but may not reach this resource — a `sending_access` key outside the send paths, a test-mode key writing live state, or an account on hold (`FORBIDDEN`, `ACCOUNT_RESTRICTED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such resource on this organization (`NOT_FOUND`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded (`RATE_LIMITED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Unexpected server error (`INTERNAL_ERROR`). Carries `request_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /audiences/{id}/contacts/batch:
    post:
      operationId: batchCreateContacts
      summary: Upsert up to 1,000 contacts
      description: For importing a list. Addresses are lowercased and de-duplicated server-side. An import can add an opt-out but never remove one.
      tags:
      - Contacts
      parameters:
      - name: id
        in: path
        required: true
        description: The audience id.
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchContactsRequest'
      responses:
        '201':
          description: Imported.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BatchContactsResponse'
        '400':
          description: The request body or query failed validation (`VALIDATION_ERROR`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing, malformed, or revoked API key (`UNAUTHORIZED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The import would exceed the plan contact limit (`PLAN_LIMIT_EXCEEDED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such resource on this organization (`NOT_FOUND`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded (`RATE_LIMITED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Unexpected server error (`INTERNAL_ERROR`). Carries `request_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /audiences/{id}/contacts/batch-delete:
    post:
      operationId: batchDeleteContacts
      summary: Delete up to 1,000 contacts by id
      description: 'Removes contacts from the audience without suppressing them. To stop mailing someone while keeping the record, update the contact with `unsubscribed: true` instead.'
      tags:
      - Contacts
      parameters:
      - name: id
        in: path
        required: true
        description: The audience id.
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/BatchDeleteContactsRequest'
      responses:
        '200':
          description: Deleted.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DeletedCountResponse'
        '400':
          description: The request body or query failed validation (`VALIDATION_ERROR`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing, malformed, or revoked API key (`UNAUTHORIZED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The key exists but may not reach this resource — a `sending_access` key outside the send paths, a test-mode key writing live state, or an account on hold (`FORBIDDEN`, `ACCOUNT_RESTRICTED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such resource on this organization (`NOT_FOUND`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded (`RATE_LIMITED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Unexpected server error (`INTERNAL_ERROR`). Carries `request_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /audiences/{id}/contacts/{contactId}:
    get:
      operationId: getContact
      summary: Get a contact
      description: One contact in an audience.
      tags:
      - Contacts
      parameters:
      - name: id
        in: path
        required: true
        description: The audience id.
        schema:
          type: string
          format: uuid
      - name: contactId
        in: path
        required: true
        description: The contact id.
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: The contact.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '401':
          description: Missing, malformed, or revoked API key (`UNAUTHORIZED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The key exists but may not reach this resource — a `sending_access` key outside the send paths, a test-mode key writing live state, or an account on hold (`FORBIDDEN`, `ACCOUNT_RESTRICTED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such resource on this organization (`NOT_FOUND`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded (`RATE_LIMITED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Unexpected server error (`INTERNAL_ERROR`). Carries `request_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    patch:
      operationId: updateContact
      summary: Update a contact
      description: Change the name, or set `unsubscribed` to opt the contact in or out.
      tags:
      - Contacts
      parameters:
      - name: id
        in: path
        required: true
        description: The audience id.
        schema:
          type: string
          format: uuid
      - name: contactId
        in: path
        required: true
        description: The contact id.
        schema:
          type: string
          format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateContactRequest'
      responses:
        '200':
          description: Updated.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Contact'
        '400':
          description: The request body or query failed validation (`VALIDATION_ERROR`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '401':
          description: Missing, malformed, or revoked API key (`UNAUTHORIZED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The key exists but may not reach this resource — a `sending_access` key outside the send paths, a test-mode key writing live state, or an account on hold (`FORBIDDEN`, `ACCOUNT_RESTRICTED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such resource on this organization (`NOT_FOUND`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded (`RATE_LIMITED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Unexpected server error (`INTERNAL_ERROR`). Carries `request_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: deleteContact
      summary: Remove a contact
      description: Removes the contact from the audience. This is not an unsubscribe.
      tags:
      - Contacts
      parameters:
      - name: id
        in: path
        required: true
        description: The audience id.
        schema:
          type: string
          format: uuid
      - name: contactId
        in: path
        required: true
        description: The contact id.
        schema:
          type: string
          format: uuid
      responses:
        '200':
          description: Removed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmptyResponse'
        '401':
          description: Missing, malformed, or revoked API key (`UNAUTHORIZED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '403':
          description: The key exists but may not reach this resource — a `sending_access` key outside the send paths, a test-mode key writing live state, or an account on hold (`FORBIDDEN`, `ACCOUNT_RESTRICTED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '404':
          description: No such resource on this organization (`NOT_FOUND`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '429':
          description: Rate limit exceeded (`RATE_LIMITED`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
        '500':
          description: Unexpected server error (`INTERNAL_ERROR`). Carries `request_id`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    CreateContactRequest:
      type: object
      required:
      - email
      additionalProperties: false
      properties:
        email:
          type: string
          format: email
        first_name:
          type: string
          maxLength: 100
        last_name:
          type: string
          maxLength: 100
        properties:
          type: object
          description: Custom properties, available as `{{key}}` in a broadcast body. Keys are lowercase letters, digits and underscores, starting with a letter (max 40 characters, 20 properties per contact). `email`, `name`, `first_name`, `last_name` and `full_name` are built in and are rejected here.
          additionalProperties:
            type: string
            maxLength: 500
    UpdateContactRequest:
      type: object
      additionalProperties: false
      properties:
        first_name:
          type: string
          maxLength: 100
        last_name:
          type: string
          maxLength: 100
        unsubscribed:
          type: boolean
          description: Set true to opt the contact out, false to resubscribe.
        properties:
          type: object
          description: Replaces the contact's custom properties. Omit to leave them unchanged; send `{}` to clear them.
          additionalProperties:
            type: string
            maxLength: 500
    Error:
      type: object
      description: Every non-2xx response from the API has this shape. `code` is stable and safe to branch on; `error` is a human sentence that may change.
      required:
      - error
      - code
      properties:
        error:
          type: string
          description: Human-readable explanation of the failure.
        code:
          type: string
          enum:
          - UNAUTHORIZED
          - FORBIDDEN
          - NOT_FOUND
          - VALIDATION_ERROR
          - RATE_LIMITED
          - MONTHLY_LIMIT_EXCEEDED
          - DAILY_LIMIT_EXCEEDED
          - PLAN_LIMIT_EXCEEDED
          - ALL_SUPPRESSED
          - RECIPIENT_DOMAIN_UNDELIVERABLE
          - DOMAIN_NOT_VERIFIED
          - SENDING_SUSPENDED
          - ACCOUNT_RESTRICTED
          - WORKSPACE_INACTIVE
          - SENDER_NOT_PERMITTED
          - SERVICE_PAUSED
          - LIST_SEND_HELD
          - BROADCAST_HELD
          - ATTACHMENT_STORAGE_ERROR
          - INTERNAL_ERROR
          - CONFLICT
          - BAD_REQUEST
          - PAYLOAD_TOO_LARGE
          description: Stable machine-readable error code.
        request_id:
          type: string
          description: Correlation id, returned on 500s and echoed in the `X-Request-Id` header on every response. Quote it in support requests.
    EmptyResponse:
      type: object
      description: An empty object. The resource is gone.
      additionalProperties: false
    ListContactsResponse:
      type: object
      required:
      - data
      - nextCursor
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/Contact'
        nextCursor:
          type:
          - string
          - 'null'
          format: uuid
    BatchContactsRequest:
      type: object
      required:
      - contacts
      additionalProperties: false
      properties:
        contacts:
          type: array
          minItems: 1
          maxItems: 1000
          items:
            type: object
            required:
            - email
            additionalProperties: false
            properties:
              email:
                type: string
                format: email
              first_name:
                type: string
                maxLength: 100
              last_name:
                type: string
                maxLength: 100
              unsubscribed:
                type: boolean
                description: Carries an opt-out over from another provider. An import can only ever add an opt-out — `false` will not resubscribe someone.
              created_at:
                type: string
                format: date-time
                description: Original signup time. Applied on insert only.
              properties:
                type: object
                description: Custom properties, usable as `{{key}}` in a broadcast body. Unlike the single-contact endpoint, an import MERGES these into whatever the contact already has, so a CSV carrying only `plan` will not drop a `company` an earlier import set.
                additionalProperties:
                  type: string
                  maxLength: 500
    Contact:
      type: object
      required:
      - id
      - audienceId
      - email
      - status
      - createdAt
      properties:
        id:
          type: string
          format: uuid
        audienceId:
          type: string
          format: uuid
        email:
          type: string
          format: email
        firstName:
          type:
          - string
          - 'null'
        lastName:
          type:
          - string
          - 'null'
        properties:
          type: object
          description: Custom properties, usable as `{{key}}` in a broadcast body.
          additionalProperties:
            type: string
        status:
          type: string
          enum:
          - subscribed
          - unsubscribed
        unsubscribedAt:
          type:
          - string
          - 'null'
          format: date-time
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type:
          - string
          - 'null'
          format: date-time
    BatchContactsResponse:
      type: object
      required:
      - count
      - duplicates
      properties:
        count:
          type: integer
          description: Rows written.
        duplicates:
          type: integer
          description: Repeated addresses collapsed before the write.
    DeletedCountResponse:
      type: object
      required:
      - deleted
      properties:
        deleted:
          type: integer
          description: May be lower than what was asked for — an id may already be gone. Retries settle at 0 rather than failing.
    BatchDeleteContactsRequest:
      type: object
      required:
      - contact_ids
      additionalProperties: false
      properties:
        contact_ids:
          type: array
          minItems: 1
          maxItems: 1000
          items:
            type: string
            format: uuid
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'An eusend API key, sent as `Authorization: Bearer eu_live_…`. Create one in the dashboard under API keys.'
externalDocs:
  description: eusend documentation
  url: https://eusend.dev/docs