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: 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