Aptly Contacts API
The Contacts API from Aptly — 5 operation(s) for contacts.
The Contacts API from Aptly — 5 operation(s) for contacts.
openapi: 3.0.3
info:
title: Aptly App Contacts API
version: '1.0'
description: 'The Aptly API lets you read and write cards on any Aptly board from external systems.
All requests require an API key passed as the `x-token` header.
API keys are scoped to your company and work across all boards.
'
servers:
- url: https://core-api.getaptly.com
description: Production
security:
- ApiKeyHeader: []
tags:
- name: Contacts
paths:
/api/contacts:
get:
summary: List contacts
description: 'Returns a paginated list of contacts scoped to your company. All filter params are optional and ANDed together.
'
operationId: listContacts
tags:
- Contacts
security:
- ApiKeyHeader: []
- DelegateToken: []
parameters:
- name: page
in: query
required: true
schema:
type: integer
minimum: 0
description: Zero-based page index.
- name: contact_type
in: query
schema:
type: string
description: Filter by contact type name.
- name: email
in: query
schema:
type: string
description: Filter by exact email address (case-insensitive).
- name: phone
in: query
schema:
type: string
description: Filter by phone number (digits only, partial match).
- name: name
in: query
schema:
type: string
description: Filter by full name (case-insensitive, all words must match).
- name: updated_after
in: query
schema:
type: string
format: date-time
description: Return only contacts updated after this ISO 8601 timestamp.
- name: updated_before
in: query
schema:
type: string
format: date-time
description: Return only contacts updated before this ISO 8601 timestamp.
responses:
'200':
description: Paginated contact list.
headers:
x-offset:
schema:
type: integer
description: Current page index.
x-count:
schema:
type: integer
description: Total matching contacts.
x-size:
schema:
type: integer
description: Page size.
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
type: object
count:
type: integer
page:
type: integer
pageSize:
type: integer
'400':
description: Missing or invalid parameters.
'401':
description: Invalid or missing API key.
post:
summary: Create or update a contact
description: 'Creates a new contact or updates an existing one (upsert).
**Lookup order:**
1. If `_id` is provided, finds by ID.
2. Otherwise finds by first email address.
3. If no match is found, creates a new contact.
**Body formats** — either native or legacy (capitalized keys) are accepted:
*Native:* `firstname`, `lastname`, `email` (string or array), `phone` (array of `{number, type}`),
`typeId`, `contactType`, `isCompany`, `title`, `company`, `imageUrl`, `customFields`
*Legacy:* `"First Name"`, `"Last Name"`, `Email`, `"Mobile Phone"`, `"Work Phone"`,
`"Home Phone"`, `"Contact Type"`, `Title`, `Company`
Returns the enriched contact with custom fields populated by their type definitions.
'
operationId: upsertContact
tags:
- Contacts
security:
- ApiKeyHeader: []
- DelegateToken: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
_id:
type: string
description: Existing contact ID — when provided, updates that contact.
firstname:
type: string
lastname:
type: string
email:
oneOf:
- type: string
- type: array
items:
type: string
description: One or more email addresses.
phone:
type: array
items:
type: object
properties:
number:
type: string
type:
type: string
enum:
- mobile
- work
- home
typeId:
type: string
description: Contact type ID.
contactType:
type: string
description: Contact type name (alternative to `typeId` — resolved to an ID automatically).
isCompany:
type: boolean
title:
type: string
company:
type: string
imageUrl:
type: string
description: Absolute URL to a JPG or PNG photo.
customFields:
type: object
description: Map of custom field ID to value. Unknown field IDs are rejected.
example:
firstname: Jane
lastname: Smith
email: jane@example.com
phone:
- number: '+15555550100'
type: mobile
contactType: Tenant
responses:
'200':
description: Contact created or updated.
content:
application/json:
schema:
$ref: '#/components/schemas/Contact'
'400':
description: Invalid input (bad URL, unknown custom field, invalid date, etc.).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Invalid or missing API key.
/api/contacts/by-email:
post:
summary: Look up contacts by email
description: 'Returns contacts whose email address matches one or more of the provided values.
Matching is case-insensitive and exact. Results are scoped to your company.
'
operationId: getContactsByEmail
tags:
- Contacts
security:
- ApiKeyHeader: []
- DelegateToken: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
properties:
email:
oneOf:
- type: string
- type: array
items:
type: string
description: A single email address or an array of email addresses to look up.
limit:
type: integer
default: 20
description: Maximum number of results to return.
skip:
type: integer
default: 0
description: Number of results to skip (for pagination).
example:
email: jane@example.com
limit: 20
skip: 0
responses:
'200':
description: Matching contacts.
content:
application/json:
schema:
type: object
properties:
contacts:
type: array
items:
$ref: '#/components/schemas/Contact'
'400':
description: Missing or invalid email field.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Invalid or missing API key.
/api/contacts/verify-email:
post:
summary: Initiate contact email verification
description: 'Looks up an email address against your org''s contact database. If a match is found,
generates a cryptographically strong 6-digit code, sends it to the address, and
returns a `requestId` and `verifyUrl` to complete the verification.
The code expires after 10 minutes and can only be used once.
'
operationId: initiateContactVerification
tags:
- Contacts
security:
- ApiKeyHeader: []
- DelegateToken: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- email
properties:
email:
type: string
description: The email address to verify.
emailSubject:
type: string
description: Subject line for the verification email. Defaults to "Your verification code".
replyTo:
type: string
description: Reply-To address for the verification email.
emailHtml:
type: string
description: 'Custom HTML body for the verification email. Supports two placeholders:
`{{ verificationCode }}` — replaced with the 6-digit code.
`{{ expirationTime }}` — replaced with the expiry duration (e.g. "10 minutes").
'
example:
email: jane@example.com
emailSubject: Your access code
replyTo: support@example.com
emailHtml: <p>Your code is <strong>{{ verificationCode }}</strong>. It expires in {{ expirationTime }}.</p>
responses:
'200':
description: Verification initiated — code sent to the email address.
content:
application/json:
schema:
type: object
properties:
requestId:
type: string
description: Opaque ID used to complete the verification.
verifyUrl:
type: string
description: API path to POST the code to (relative URL).
example:
requestId: a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
verifyUrl: /api/contacts/verify-email/a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4/confirm
'400':
description: Missing or invalid email field.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Invalid or missing API key.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'404':
description: No contact found with that email address.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Verification request was created but the email could not be delivered (EMAIL_SEND_FAILURE).
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/contacts/verify-email/{requestId}/confirm:
post:
summary: Confirm contact email verification
description: 'Submits the 6-digit code received by email. Returns the matching contact records
if the code is valid, not expired, and has not already been used.
After 5 consecutive failed attempts the verification is permanently invalidated.
The caller must re-initiate a new verification to try again.
'
operationId: confirmContactVerification
tags:
- Contacts
security:
- ApiKeyHeader: []
- DelegateToken: []
parameters:
- name: requestId
in: path
required: true
schema:
type: string
description: The `requestId` returned by the initiate endpoint.
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- code
properties:
code:
type: string
description: The 6-digit verification code sent to the email address.
example:
code: 042815
responses:
'200':
description: Code accepted — returns verified status and matching contacts.
content:
application/json:
schema:
type: object
properties:
verified:
type: boolean
example: true
contacts:
type: array
items:
$ref: '#/components/schemas/Contact'
'400':
description: Missing code field.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Invalid or missing API key, or code is invalid/expired/already used.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/api/contacts/{contactId}:
get:
summary: Get a contact
description: Returns a single contact by its ID, with custom fields enriched by their type definitions.
operationId: getContact
tags:
- Contacts
security:
- ApiKeyHeader: []
- DelegateToken: []
parameters:
- name: contactId
in: path
required: true
schema:
type: string
description: The contact's `_id`.
responses:
'200':
description: Contact record.
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Contact'
count:
type: integer
page:
type: integer
pageSize:
type: integer
'401':
description: Invalid or missing API key.
post:
summary: Update a contact
description: 'Updates an existing contact by ID using the same upsert logic as `POST /api/contacts`.
The `_id` is taken from the URL — any `_id` in the body is ignored.
Accepts the same **native** or **legacy** body formats as `POST /api/contacts`.
Returns the updated, enriched contact.
'
operationId: updateContact
tags:
- Contacts
security:
- ApiKeyHeader: []
- DelegateToken: []
parameters:
- name: contactId
in: path
required: true
schema:
type: string
description: The contact's `_id`.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
firstname:
type: string
lastname:
type: string
email:
oneOf:
- type: string
- type: array
items:
type: string
description: One or more email addresses.
phone:
type: array
items:
type: object
properties:
number:
type: string
type:
type: string
enum:
- mobile
- work
- home
typeId:
type: string
description: Contact type ID.
contactType:
type: string
description: Contact type name (alternative to `typeId`).
isCompany:
type: boolean
title:
type: string
company:
type: string
imageUrl:
type: string
description: Absolute URL to a JPG or PNG photo.
customFields:
type: object
description: Map of custom field ID to value.
example:
firstname: Jane
lastname: Smith
email: jane@example.com
responses:
'200':
description: Updated contact.
content:
application/json:
schema:
$ref: '#/components/schemas/Contact'
'400':
description: Invalid input.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'401':
description: Invalid or missing API key.
components:
schemas:
Contact:
type: object
properties:
_id:
type: string
description: MongoDB ObjectId of the contact.
uuid:
type: string
firstname:
type: string
lastname:
type: string
fullName:
type: string
description: Computed display name (first + last, or company name for org records).
duogram:
type: string
description: Two-letter initials derived from first and last name.
photoId:
type: string
nullable: true
imageUrl:
type: string
nullable: true
description: CDN thumbnail URL for the contact's photo, or null if none.
email:
type: string
phone:
type: string
typeId:
type: string
companyId:
type: string
address:
type: object
title:
type: string
alerts:
type: array
items:
type: object
company:
type: string
description: Company name — populated when isCompany is true.
isCompany:
type: boolean
Error:
type: object
properties:
error:
type: string
message:
type: string
securitySchemes:
ApiKeyHeader:
type: apiKey
in: header
name: x-token
DelegateToken:
type: apiKey
in: header
name: Authorization
description: 'Delegate token issued by the platform. Format: `DelegateToken <token>`'
PartnerBearer:
type: http
scheme: bearer
description: 'Partner token. Format: `Authorization: Bearer <token>`'