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/knak-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 email required.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Knak Send API Reference Contacts API
version: V1
x-logo:
url: https://s3.amazonaws.com/assets.knak.io/img/Knak-Logo-Medium.png
contact:
email: support@knak.com
description: "# Overview\nWelcome to the developer documentation for the Knak Send API.\nWe provide a RESTful interface to key resources within Knak Send to enable your own automation workflows.\nThis API will allow you to automate processes regarding contact and field management within your Knak Send environment.\nYou can download the formal definition of this public interface in OpenAPI 3 (formerly Swagger) format using the link above.\n\n## Endpoint\n`https://send.knak.io/api/public/v1`\n\n## Authentication\nAll requests are authenticated using a Bearer token in the `Authorization` header:\n```\ncurl --location --request GET 'https://send.knak.io/api/public/v1/contacts' \\\n--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbG...'\n```\nA token can be created as a non-expiring token through the Enterprise UI, via the [API Access menu](https://enterprise.knak.io/account/api-access). The account associated with the token must have Knak Send access enabled.\n\n## Errors\nErrors in requests made to the API can be viewed directly from the response code that is returned.\n\nBelow are a list of the common error responses returned and an explanation of what they mean.\n\n| Code | Reason |Description |\n| ----------------| ----------- | ----------- |\n| **400** | **Bad Request** | The request was rejected (for example, a contact's email domain is not in the account's allow list). |\n| **401** | **Unauthenticated** | The access token is missing, invalid, or expired. |\n| **403** | **Forbidden** | The account does not have Knak Send access enabled, or the token lacks the required scope. |\n| **404** | **Not Found** | The requested resource could not be found. Verify that the resource exists and that you are using the correct identifier. |\n| **409** | **Conflict** | The resource could not be created because it already exists (for example, a field with the same generated key). |\n| **422** | **Unprocessable Entity** | One or more request fields failed backend validation. The `meta` object lists the offending fields. |\n| **429** | **Too Many Requests** | The rate limit for the endpoint has been exceeded. |\n| **503** | **Service Unavailable** | An upstream authentication service is temporarily unavailable. This can occur on any request; retry after a short delay. |\n\nThree error response shapes are used. Where an error `code` (or `identifier`) is present, branch on it\nprogrammatically; the human-readable `message`/`details` text is subject to change.\n\nAuthentication, authorization, bad-request, not-found, conflict and service errors\n(400, 401, 403, 404, 409, 503) return a compact `error` object with a machine-readable `code`:\n```json\n{ \"error\": { \"code\": \"UNAUTHENTICATED\", \"message\": \"Invalid or expired access token.\" } }\n```\nValidation errors (422) return an `errors` array:\n```json\n{\n \"error_at\": \"2026-06-02T13:56:31+00:00\",\n \"errors\": [\n {\n \"identifier\": \"ValidationError\",\n \"details\": \"One or more of the given request fields failed backend validation.\",\n \"meta\": { \"email\": [\"The email field is required.\"] }\n }\n ]\n}\n```\nRate-limit (429) errors return a `message`/`type` object:\n```json\n{ \"message\": \"Too Many Attempts.\", \"type\": \"HttpException\" }\n```\n\n## Rate limiting\nRequests are rate limited per API client. Standard endpoints allow up to **1000 requests per minute**.\nBulk endpoints (such as `POST /contacts/bulk`) are limited to **60 requests per minute**. Exceeding a limit\nreturns a `429 Too Many Requests` response.\n\n## Pagination\nList endpoints that can return large result sets are paginated using the `page` query parameter:\n\n- `page[number]` — the page to return (1-based, default `1`).\n- `page[size]` — the number of records per page (default `25`, maximum `100`).\n\nPaginated responses include a `meta` object describing the result window (`total`, `current_page`,\n`last_page`, `per_page`, `from`, `to`).\n\n## Filtering\nList endpoints support filtering on specific fields using the `filter` query parameter, in the form\n`filter[field_name]=value`. The fields that can be filtered are listed on each endpoint.\n\nFor simple equality, pass the value directly: `filter[first_name]=Jane`. For other comparisons, pass an\n`operator` and `value`: `filter[first_name][operator]=startsWith&filter[first_name][value]=Ja`. Supported\noperators include `=`, `!=`, `>`, `<`, `~`, `contains`, `notContains`, `startsWith`, `between`, `exists`\nand `notExists`.\n\n## Sorting\nList endpoints support sorting via the `sort` query parameter. Pass one or more field names; prefix a field\nwith `-` for descending order. For example `sort[]=last_name&sort[]=-created_at`. The fields that can be\nsorted are listed on each endpoint.\n"
servers:
- url: https://send.knak.io/api/public/v1
description: production
security:
- bearerAuth: []
tags:
- name: Contacts
description: Create, retrieve and list contacts in Knak Send.
paths:
/contacts:
get:
tags:
- Contacts
summary: List contacts
description: 'Returns a paginated list of contacts. Supports pagination, sorting and filtering.
- **Sortable fields:** `email`, `first_name`, `last_name`, `created_at`
- **Filterable fields:** `email`, `first_name`, `last_name`
'
operationId: listContacts
parameters:
- $ref: '#/components/parameters/PageNumber'
- $ref: '#/components/parameters/PageSize'
- name: sort[]
in: query
required: false
description: 'Fields to sort by, passed as a repeated `sort[]` parameter. Prefix a field with `-` for descending order. Allowed fields: `email`, `first_name`, `last_name`, `created_at`.'
style: form
explode: true
schema:
type: array
items:
type: string
enum:
- email
- first_name
- last_name
- created_at
- -email
- -first_name
- -last_name
- -created_at
example:
- -created_at
- name: filter[email]
in: query
required: false
description: Filter by email address.
schema:
type: string
example: jane.doe@example.com
- name: filter[first_name]
in: query
required: false
description: Filter by first name.
schema:
type: string
- name: filter[last_name]
in: query
required: false
description: Filter by last name.
schema:
type: string
responses:
'200':
description: A paginated list of contacts.
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Contact'
meta:
$ref: '#/components/schemas/PaginationMeta'
examples:
default:
value:
data:
- id: 9b1c2d3e-4f56-7890-abcd-ef1234567890
first_name: Jane
last_name: Doe
email: jane.doe@example.com
avatar_url: https://cdn.example.com/avatars/jane.png
supervisor_id: 1a2b3c4d-5e6f-7890-abcd-ef0987654321
created_at: '2026-05-01T12:00:00+00:00'
updated_at: '2026-05-20T09:30:00+00:00'
meta:
total: 1
current_page: 1
last_page: 1
per_page: 25
from: 1
to: 1
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'422':
$ref: '#/components/responses/ValidationError'
'429':
$ref: '#/components/responses/TooManyRequests'
post:
tags:
- Contacts
summary: Create a contact
description: Creates a single contact. To create or update many contacts at once, use `POST /contacts/bulk`.
operationId: createContact
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ContactInput'
examples:
default:
value:
email: jane.doe@example.com
first_name: Jane
last_name: Doe
avatar_url: https://cdn.example.com/avatars/jane.png
field_values:
department: Engineering
startDate: '2026-01-15'
responses:
'201':
description: The contact was created.
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/Contact'
'400':
$ref: '#/components/responses/EmailDomainNotAllowed'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'409':
$ref: '#/components/responses/ContactConflict'
'422':
$ref: '#/components/responses/ValidationError'
'429':
$ref: '#/components/responses/TooManyRequests'
/contacts/{id}:
get:
tags:
- Contacts
summary: Retrieve a contact
description: Returns a single contact by ID, including its field values.
operationId: getContact
parameters:
- $ref: '#/components/parameters/ContactId'
responses:
'200':
description: The requested contact.
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/ContactDetail'
examples:
default:
value:
data:
id: 9b1c2d3e-4f56-7890-abcd-ef1234567890
first_name: Jane
last_name: Doe
email: jane.doe@example.com
avatar_url: https://cdn.example.com/avatars/jane.png
supervisor_id: 1a2b3c4d-5e6f-7890-abcd-ef0987654321
created_at: '2026-05-01T12:00:00+00:00'
updated_at: '2026-05-20T09:30:00+00:00'
field_values:
- field_id: f1e2d3c4-b5a6-7890-abcd-ef1234567890
key: department
name: Department
type: string
value: Engineering
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
delete:
tags:
- Contacts
summary: Delete a contact
description: Soft-deletes a contact by ID. The contact is removed from listings but retained internally.
operationId: deleteContact
parameters:
- $ref: '#/components/parameters/ContactId'
responses:
'204':
description: The contact was deleted. The response body is empty.
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
/contacts/{id}/field-values:
get:
tags:
- Contacts
summary: List a contact's field values
description: Returns the field values associated with a single contact.
operationId: getContactFieldValues
parameters:
- $ref: '#/components/parameters/ContactId'
responses:
'200':
description: The contact's field values.
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/FieldValue'
examples:
default:
value:
data:
- field_id: f1e2d3c4-b5a6-7890-abcd-ef1234567890
key: department
name: Department
type: string
value: Engineering
- field_id: a9b8c7d6-e5f4-3210-abcd-ef0987654321
key: startDate
name: Start Date
type: date
value: '2026-01-15'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/TooManyRequests'
put:
tags:
- Contacts
summary: Replace a contact's field values
description: 'Replaces the field values for a single contact. Each key in `field_values` must match the `key` of an
existing field (see `GET /fields`) and its value is validated against the field''s type. Between 1 and 20
field values may be provided.
'
operationId: replaceContactFieldValues
parameters:
- $ref: '#/components/parameters/ContactId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/FieldValuesInput'
examples:
default:
value:
field_values:
department: Engineering
startDate: '2026-01-15'
responses:
'200':
description: The contact's updated field values.
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/FieldValue'
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'422':
$ref: '#/components/responses/ValidationError'
'429':
$ref: '#/components/responses/TooManyRequests'
/contacts/bulk:
post:
tags:
- Contacts
summary: Bulk upsert contacts
description: 'Creates or updates up to 1000 contacts in a single request, matching existing contacts by email.
Validation is performed per contact. The request as a whole succeeds with a `200` response even when
some individual contacts fail; the response body reports counts of `created`, `updated` and `failed`
contacts, along with an `errors` array describing each failure by its index in the submitted array.
This endpoint is subject to the stricter bulk rate limit (60 requests per minute).
'
operationId: bulkUpsertContacts
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- contacts
properties:
contacts:
type: array
minItems: 1
maxItems: 1000
items:
$ref: '#/components/schemas/ContactInput'
examples:
default:
value:
contacts:
- email: jane.doe@example.com
first_name: Jane
last_name: Doe
field_values:
department: Engineering
- email: john@notallowed.com
first_name: John
last_name: Smith
responses:
'200':
description: 'The bulk operation was processed. Inspect `created`, `updated`, `failed` and `errors` to determine
the outcome of each contact.
'
content:
application/json:
schema:
type: object
properties:
data:
$ref: '#/components/schemas/BulkUpsertResult'
examples:
default:
value:
data:
created: 1
updated: 0
failed: 1
errors:
- index: 1
email: john@notallowed.com
details:
email:
- Domain is not allowed
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'422':
$ref: '#/components/responses/ValidationError'
'429':
$ref: '#/components/responses/TooManyRequests'
delete:
tags:
- Contacts
summary: Bulk delete contacts
description: 'Deletes up to 1000 contacts in a single request, identified by their IDs. The response reports the
number of contacts actually deleted; IDs that do not match an existing contact are ignored rather than
causing the request to fail.
This endpoint is subject to the stricter bulk rate limit (60 requests per minute).
'
operationId: bulkDeleteContacts
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- ids
properties:
ids:
type: array
minItems: 1
maxItems: 1000
items:
type: string
maxLength: 255
examples:
default:
value:
ids:
- 9b1c2d3e-4f56-7890-abcd-ef1234567890
- 1a2b3c4d-5e6f-7890-abcd-ef0987654321
responses:
'200':
description: The bulk delete was processed.
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
deleted:
type: integer
description: The number of contacts that were deleted.
examples:
default:
value:
data:
deleted: 2
'401':
$ref: '#/components/responses/Unauthenticated'
'403':
$ref: '#/components/responses/Forbidden'
'422':
$ref: '#/components/responses/ValidationError'
'429':
$ref: '#/components/responses/TooManyRequests'
components:
schemas:
FieldType:
type: string
description: The data type of a field.
enum:
- string
- number
- date
- datetime
- boolean
PaginationMeta:
type: object
description: Pagination metadata describing the current result window.
properties:
total:
type: integer
description: The total number of matching records.
current_page:
type: integer
last_page:
type: integer
per_page:
type: integer
from:
type:
- integer
- 'null'
description: The index of the first record on the current page.
to:
type:
- integer
- 'null'
description: The index of the last record on the current page.
HttpError:
type: object
description: The error shape returned for rate-limit (429) errors.
properties:
message:
type: string
description: A human-readable description of the error.
type:
type: string
description: The error type. Always `HttpException` for this shape.
FieldValuesInput:
type: object
description: The payload used to replace a contact's field values.
required:
- field_values
properties:
field_values:
type: object
minProperties: 1
maxProperties: 20
description: 'A map of field key to scalar value. Each key must match the `key` of an existing field
(see `GET /fields`). Between 1 and 20 entries may be provided. Replaces the contact''s existing
field values.
'
additionalProperties:
type:
- string
- number
- boolean
example:
department: Engineering
startDate: '2026-01-15'
Contact:
type: object
description: A contact as returned in list and create responses.
properties:
id:
type: string
description: The unique identifier of the contact.
first_name:
type: string
last_name:
type: string
email:
type: string
format: email
avatar_url:
type:
- string
- 'null'
format: uri
description: URL of the contact's avatar image, if set.
supervisor_id:
type:
- string
- 'null'
description: The ID of this contact's supervisor, if one is assigned.
created_at:
type:
- string
- 'null'
format: date-time
description: ISO 8601 creation timestamp.
updated_at:
type:
- string
- 'null'
format: date-time
description: ISO 8601 last-updated timestamp.
ContactDetail:
type: object
description: A contact with its associated field values, returned when retrieving a single contact.
allOf:
- $ref: '#/components/schemas/Contact'
- type: object
properties:
field_values:
type: array
items:
$ref: '#/components/schemas/FieldValue'
FieldValue:
type: object
description: A single field value belonging to a contact.
properties:
field_id:
type: string
description: The ID of the field this value belongs to.
key:
type:
- string
- 'null'
description: The generated key of the field.
name:
type:
- string
- 'null'
description: The human-readable name of the field.
type:
description: The data type of the field. May be `null` if the field's metadata is unavailable.
oneOf:
- $ref: '#/components/schemas/FieldType'
- type: 'null'
value:
type: string
description: The field value, represented as a string.
ContactInput:
type: object
description: The payload used to create or upsert a contact.
required:
- email
- first_name
- last_name
properties:
email:
type: string
format: email
maxLength: 255
description: The contact's email address. Used as the match key for bulk upserts.
first_name:
type: string
maxLength: 255
last_name:
type: string
maxLength: 255
avatar_url:
type:
- string
- 'null'
format: uri
description: 'URL of the contact''s avatar image. For `POST /contacts` this must be a valid HTTP(S) URL.
For bulk upserts (`POST /contacts/bulk`) the value is accepted as any string and is not validated
as a URL.
'
field_values:
type: object
description: 'A map of field key to scalar value. Each key must match the `key` of an existing field
(see `GET /fields`). Values are validated against the field''s type.
'
additionalProperties:
type:
- string
- number
- boolean
example:
department: Engineering
startDate: '2026-01-15'
ErrorList:
type: object
description: The error shape returned for validation and not-found errors.
properties:
error_at:
type: string
format: date-time
description: ISO 8601 timestamp of when the error occurred.
errors:
type: array
items:
type: object
properties:
identifier:
type: string
description: A machine-readable identifier for the error type.
details:
type: string
description: A human-readable description of the error.
meta:
type: object
description: Additional context. For validation errors, a map of field name to validation messages.
BulkUpsertResult:
type: object
description: The outcome of a bulk upsert operation.
properties:
created:
type: integer
description: The number of contacts created.
updated:
type: integer
description: The number of existing contacts updated.
failed:
type: integer
description: The number of contacts that failed validation or processing.
errors:
type: array
description: One entry per failed contact.
items:
type: object
properties:
index:
type: integer
description: The 0-based position of the contact in the submitted `contacts` array.
email:
type:
- string
- 'null'
description: The email of the failed contact, if available.
details:
type: object
description: A map of field name to an array of validation messages.
additionalProperties:
type: array
items:
type: string
ErrorObject:
type: object
description: The compact error shape returned for authentication, authorization and conflict errors.
properties:
error:
type: object
properties:
code:
type: string
description: A machine-readable error code.
message:
type: string
description: A human-readable description of the error.
responses:
TooManyRequests:
description: 'The rate limit for the endpoint has been exceeded. A `Retry-After` header indicates how many seconds
to wait before retrying.
'
content:
application/json:
schema:
$ref: '#/components/schemas/HttpError'
example:
message: Too Many Attempts.
type: HttpException
EmailDomainNotAllowed:
description: The contact's email domain is not in the account's allow list.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorObject'
example:
error:
code: EMAIL_DOMAIN_NOT_ALLOWED
message: The address john@notallowed.com has a domain that is not in your allow list.
ContactConflict:
description: A contact with the given email already exists.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorObject'
example:
error:
code: CONTACT_ALREADY_EXISTS
message: A contact with this email already exists.
Unauthenticated:
description: The access token is missing, invalid or expired.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorObject'
example:
error:
code: UNAUTHENTICATED
message: Invalid or expired access token.
ValidationError:
description: One or more request fields failed backend validation.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorList'
example:
error_at: '2026-06-02T13:56:31+00:00'
errors:
- identifier: ValidationError
details: One or more of the given request fields failed backend validation.
meta:
email:
- The email field is required.
Forbidden:
description: Knak Send access is not enabled for the account, or the token lacks the required scope.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorObject'
example:
error:
code: ACCESS_DENIED
message: Knak Send access is not enabled for this account.
NotFound:
description: The requested resource could not be found.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorObject'
example:
error:
code: CONTACT_NOT_FOUND
message: Contact not found.
parameters:
ContactId:
name: id
in: path
required: true
description: The unique identifier of the contact.
schema:
type: string
example: 9b1c2d3e-4f56-7890-abcd-ef1234567890
PageSize:
name: page[size]
in: query
required: false
description: The number of records per page.
schema:
type: integer
minimum: 1
maximum: 100
default: 25
PageNumber:
name: page[number]
in: query
required: false
description: The page of results to return (1-based).
schema:
type: integer
minimum: 1
default: 1
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
description: A bearer token generated through the Enterprise API Access menu.