Reonic Contacts API
External people you interact with, such as customers or leads. Not to be confused with [**Users**](#tag/users), which represent members of your workspace.
External people you interact with, such as customers or leads. Not to be confused with [**Users**](#tag/users), which represent members of your workspace.
openapi: 3.1.0
info:
title: Reonic REST Api v3 Activities Contacts API
description: 'The Reonic REST API v3 provides programmatic access to create and manage resources. The API follows REST principles and returns responses in JSON format. Authentication is required via an API key passed in the X-Authorization header.
## Errors
All endpoints return errors with the same JSON shape:
```json
{ "message": "human-readable description" }
```
`400` responses additionally include an `errors` field with per-field validation details.
The HTTP status code identifies the cause:
| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad Request | Path params, query string, or request body failed validation. Inspect `errors` for the field-level breakdown. |
| `401` | Unauthorized | The `X-Authorization` header is missing, malformed, does not match an active API key, or belongs to a different API version. The response never indicates which check failed; check that the key matches the endpoint version. API v3 endpoints require a v3 key with the `rnc_v3_` prefix. |
| `403` | Forbidden | The API key is read-only and the request targeted a write endpoint (`POST`). Issue a key with write access. |
| `404` | Not Found | A resource referenced by a path id does not exist or is not visible to your workspace. |
| `429` | Too Many Requests | The per-client rate limit was exceeded. See **Rate limiting** below. |
| `500` | Internal Server Error | Unexpected failure. Safe to retry once; if it persists, contact support. |
| `503` | Service Unavailable | A backing dependency is temporarily unavailable. Retry with exponential backoff. |
## Rate limiting
Limits are shared across all API keys you hold and reset on a 1-minute window. Two buckets:
| Bucket | Limit | Applies to |
|--------|-------|------------|
| `cached` | 500 / min | `GET` requests served from the response cache |
| `uncached` | 30 / min | Cache misses, `GET` requests sent with `Reonic-Cache-Control: no-cache`, and all `POST` requests |
Every response includes:
- `X-RateLimit-Bucket` — `cached` or `uncached`
- `X-RateLimit-Limit` — the bucket''s ceiling (`500` or `30`)
- `X-RateLimit-Remaining` — calls left in the current window
- `X-RateLimit-Reset` — Unix epoch seconds at which the window resets
- `X-RateLimit-Policy` — `<limit>;w=60`
`429` responses additionally set `Retry-After` (in seconds). Wait at least that long before retrying.
## Caching and Reonic-Cache-Control
`GET` responses are cached for up to 1 hour. Identical requests (same path and query) on the same API key return the cached result. To force a fresh read, send `Reonic-Cache-Control: no-cache`; the response is then refreshed and re-cached. Forced refreshes count against the `uncached` rate-limit bucket.
The standard `Cache-Control` header is not honored. Use `Reonic-Cache-Control` to control caching behavior.
## Authentication
Every request must include your API key in the `X-Authorization` header:
```
X-Authorization: <your-api-key>
```
API keys are issued from the Reonic web app and look like `rnc_v3_…`. Send the full value, including the prefix.
'
version: 3.2.0
contact:
email: kontakt@reonic.de
url: https://reonic.com
name: Reonic GmbH
servers:
- url: '{apiBaseUrl}/rest/v3/'
security:
- X-Authorization: []
tags:
- name: Contacts
description: External people you interact with, such as customers or leads. Not to be confused with [**Users**](#tag/users), which represent members of your workspace.
paths:
/contacts:
get:
summary: List contacts
description: 'List contacts for the authenticated client in a paginated format.
**Allowed API keys:** Read-only, Read and Write'
tags:
- Contacts
parameters:
- schema:
type: string
format: email
description: Return only contacts whose primary or secondary email equals this address (case-insensitive exact match). Useful for matching an incoming email to a contact.
example: jane.doe@example.com
required: false
description: Return only contacts whose primary or secondary email equals this address (case-insensitive exact match). Useful for matching an incoming email to a contact.
name: email
in: query
- schema:
type: string
pattern: ^\+[1-9]\d{1,14}$
description: Return only contacts whose phone or mobile number exactly matches this number. Must be in E.164 format (e.g. +491511234567). Useful for matching an incoming call to a contact. Numbers not stored in E.164 format cannot be matched by this filter.
example: '+491511234567'
required: false
description: Return only contacts whose phone or mobile number exactly matches this number. Must be in E.164 format (e.g. +491511234567). Useful for matching an incoming call to a contact. Numbers not stored in E.164 format cannot be matched by this filter.
name: phoneNumber
in: query
- schema:
type:
- string
- 'null'
format: date-time
description: Filter records where createdAt is after this datetime (exclusive)
example: '2026-01-01T15:30:00.000Z'
required: false
description: Filter records where createdAt is after this datetime (exclusive)
name: createdAt.gt
in: query
- schema:
type:
- string
- 'null'
format: date-time
description: Filter records where createdAt is before this datetime (exclusive)
example: '2026-01-01T15:30:00.000Z'
required: false
description: Filter records where createdAt is before this datetime (exclusive)
name: createdAt.lt
in: query
- schema:
type:
- string
- 'null'
format: date-time
description: Filter records where updatedAt is after this datetime (exclusive)
example: '2026-01-01T15:30:00.000Z'
required: false
description: Filter records where updatedAt is after this datetime (exclusive)
name: updatedAt.gt
in: query
- schema:
type:
- string
- 'null'
format: date-time
description: Filter records where updatedAt is before this datetime (exclusive)
example: '2026-01-01T15:30:00.000Z'
required: false
description: Filter records where updatedAt is before this datetime (exclusive)
name: updatedAt.lt
in: query
- schema:
type: integer
minimum: 1
default: 1
description: 'Page number, starting from 1. Default: 1.'
required: false
description: 'Page number, starting from 1. Default: 1.'
name: page
in: query
- schema:
type: integer
minimum: 1
maximum: 200
default: 50
description: 'Number of items per page. Default: 50. Max: 200.'
required: false
description: 'Number of items per page. Default: 50. Max: 200.'
name: itemsPerPage
in: query
- schema:
type:
- string
- 'null'
default: -createdAt
description: 'Sort order. Use field names for ascending (e.g. "createdAt"), minus sign for descending (e.g. "-createdAt"). Chain with commas (e.g. "createdAt,editedAt"). Sortable fields: fullName, createdAt. Defaults to -createdAt.'
examples:
- -createdAt
- createdAt,-fullName
required: false
description: 'Sort order. Use field names for ascending (e.g. "createdAt"), minus sign for descending (e.g. "-createdAt"). Chain with commas (e.g. "createdAt,editedAt"). Sortable fields: fullName, createdAt. Defaults to -createdAt.'
name: sort
in: query
- schema:
type: string
example: no-cache
required: false
name: Reonic-Cache-Control
in: header
description: Set to 'no-cache' to bypass the 1-hour response cache and force a fresh read. The fresh response is then written back to the cache for subsequent callers. Forced refreshes count against the uncached rate-limit bucket (30/min); only cache hits count against the cached bucket (500/min).
responses:
'200':
description: Paginated list of contacts
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Contact'
pagination:
type: object
properties:
page:
type: integer
minimum: 1
description: Current page number
perPage:
type: integer
minimum: 1
description: Number of items per page
total:
type: integer
minimum: 0
description: Total number of items across all pages
totalPages:
type: integer
minimum: 1
next:
type:
- string
- 'null'
description: Path to the next page, or null if there is no next page
prev:
type:
- string
- 'null'
description: Path to the previous page, or null if there is no previous page
required:
- page
- perPage
- total
- totalPages
- next
- prev
required:
- data
- pagination
/contacts/{contactId}:
get:
summary: Get contact
description: 'Get a single contact by its ID.
**Allowed API keys:** Read-only, Read and Write'
tags:
- Contacts
parameters:
- schema:
type: string
format: uuid
required: true
name: contactId
in: path
- schema:
type: string
example: no-cache
required: false
name: Reonic-Cache-Control
in: header
description: Set to 'no-cache' to bypass the 1-hour response cache and force a fresh read. The fresh response is then written back to the cache for subsequent callers. Forced refreshes count against the uncached rate-limit bucket (30/min); only cache hits count against the cached bucket (500/min).
responses:
'200':
description: The contact
content:
application/json:
schema:
$ref: '#/components/schemas/ContactDetail'
/contacts/create:
post:
summary: Create contact
description: 'Create a new contact.
**Allowed API keys:** Read and Write, Lead creation only'
tags:
- Contacts
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ContactCreate'
responses:
'201':
description: The created contact
content:
application/json:
schema:
$ref: '#/components/schemas/ContactDetail'
/contacts/{contactId}/update:
post:
summary: Update contact
description: 'Update a contact by its ID.
**Allowed API keys:** Read and Write'
tags:
- Contacts
parameters:
- schema:
type: string
format: uuid
required: true
name: contactId
in: path
requestBody:
content:
application/json:
schema:
type: object
properties:
firstName:
type: string
minLength: 1
maxLength: 1000
lastName:
type: string
minLength: 1
maxLength: 1000
salutation:
type:
- string
- 'null'
enum:
- Female
- Male
- Diverse
- Family
- Business
- null
primaryEmail:
type:
- string
- 'null'
maxLength: 1000
format: email
secondaryEmail:
type:
- string
- 'null'
maxLength: 1000
format: email
description: Alternative email address
mobile:
type:
- string
- 'null'
maxLength: 100
description: Mobile phone number
phone:
type:
- string
- 'null'
maxLength: 100
description: Landline phone number
phoneReachability:
type:
- string
- 'null'
enum:
- Morning
- Afternoon
- Evening
- Fulltime
- WeekendOnly
- EmailOnly
- null
address:
type: object
properties:
street:
type:
- string
- 'null'
maxLength: 1000
houseNumber:
type:
- string
- 'null'
maxLength: 1000
city:
type:
- string
- 'null'
maxLength: 1000
postcode:
type:
- string
- 'null'
maxLength: 1000
country:
type:
- string
- 'null'
maxLength: 1000
additionalProperties: false
integrations:
type: object
properties:
bitrixId:
type:
- integer
- 'null'
minimum: 1
maximum: 9007199254740991
hubspotId:
type:
- string
- 'null'
format: uuid
pdsId:
type:
- string
- 'null'
maxLength: 1000
photovateId:
type:
- string
- 'null'
maxLength: 1000
pipedriveId:
type:
- integer
- 'null'
minimum: 1
maximum: 9007199254740991
weclappId:
type:
- string
- 'null'
format: uuid
additionalProperties: false
description: IDs of this contact in third-party systems
additionalProperties: false
responses:
'200':
description: The updated contact
content:
application/json:
schema:
$ref: '#/components/schemas/ContactDetail'
'404':
description: Contact not found
content:
application/json:
schema:
type: object
properties:
message:
type: string
required:
- message
components:
schemas:
ContactDetail:
allOf:
- $ref: '#/components/schemas/Contact'
- type: object
properties:
commercialProjectIds:
type: array
items:
type: string
format: uuid
description: IDs of commercial projects associated with this contact
example:
- 123e4567-e89b-12d3-a456-426614174000
residentialProjectIds:
type: array
items:
type: string
format: uuid
description: IDs of residential projects associated with this contact
example:
- 123e4567-e89b-12d3-a456-426614174000
marketingConsent:
type: boolean
description: Whether the contact has given marketing consent
marketingConsentText:
type:
- string
- 'null'
marketingConsentDataProtectionLink:
type:
- string
- 'null'
utm:
type: object
properties:
campaign:
type:
- string
- 'null'
content:
type:
- string
- 'null'
medium:
type:
- string
- 'null'
source:
type:
- string
- 'null'
term:
type:
- string
- 'null'
required:
- campaign
- content
- medium
- source
- term
description: UTM parameters associated with this contact
integrations:
type: object
properties:
bitrixId:
type:
- number
- 'null'
hubspotId:
type:
- string
- 'null'
format: uuid
pdsId:
type:
- string
- 'null'
photovateId:
type:
- string
- 'null'
pipedriveId:
type:
- number
- 'null'
weclappId:
type:
- string
- 'null'
format: uuid
required:
- bitrixId
- hubspotId
- pdsId
- photovateId
- pipedriveId
- weclappId
description: IDs of the contact in integrated third-party systems
required:
- commercialProjectIds
- residentialProjectIds
- marketingConsent
- marketingConsentText
- marketingConsentDataProtectionLink
- utm
- integrations
Contact:
type: object
properties:
id:
type: string
format: uuid
example: 123e4567-e89b-12d3-a456-426614174000
fullName:
type: string
description: First and last name combined
firstName:
type: string
lastName:
type: string
salutation:
type:
- string
- 'null'
enum:
- Female
- Male
- Diverse
- Family
- Business
- null
primaryEmail:
type:
- string
- 'null'
secondaryEmail:
type:
- string
- 'null'
description: Alternative email address
mobile:
type:
- string
- 'null'
description: Mobile phone number
phone:
type:
- string
- 'null'
description: Landline phone number
phoneReachability:
type:
- string
- 'null'
enum:
- Morning
- Afternoon
- Evening
- Fulltime
- WeekendOnly
- EmailOnly
- null
address:
type: object
properties:
street:
type:
- string
- 'null'
houseNumber:
type:
- string
- 'null'
city:
type:
- string
- 'null'
postcode:
type:
- string
- 'null'
country:
type:
- string
- 'null'
required:
- street
- houseNumber
- city
- postcode
- country
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
description: Last edited date
deletedAt:
type:
- string
- 'null'
format: date-time
required:
- id
- fullName
- firstName
- lastName
- salutation
- primaryEmail
- secondaryEmail
- mobile
- phone
- phoneReachability
- address
- createdAt
- updatedAt
- deletedAt
ContactCreate:
type: object
properties:
firstName:
type: string
minLength: 1
maxLength: 1000
lastName:
type: string
minLength: 1
maxLength: 1000
salutation:
type:
- string
- 'null'
enum:
- Female
- Male
- Diverse
- Family
- Business
- null
primaryEmail:
type:
- string
- 'null'
maxLength: 1000
format: email
secondaryEmail:
type:
- string
- 'null'
maxLength: 1000
format: email
description: Alternative email address
mobile:
type:
- string
- 'null'
maxLength: 100
description: Mobile phone number
phone:
type:
- string
- 'null'
maxLength: 100
description: Landline phone number
phoneReachability:
type:
- string
- 'null'
enum:
- Morning
- Afternoon
- Evening
- Fulltime
- WeekendOnly
- EmailOnly
- null
address:
type: object
properties:
street:
type:
- string
- 'null'
maxLength: 1000
houseNumber:
type:
- string
- 'null'
maxLength: 1000
city:
type:
- string
- 'null'
maxLength: 1000
postcode:
type:
- string
- 'null'
maxLength: 1000
country:
type:
- string
- 'null'
maxLength: 1000
additionalProperties: false
marketingConsent:
type: boolean
description: Whether the contact has given marketing consent
marketingConsentText:
type:
- string
- 'null'
maxLength: 10000
marketingConsentDataProtectionLink:
type:
- string
- 'null'
maxLength: 2000
utm:
type: object
properties:
campaign:
type:
- string
- 'null'
maxLength: 1000
content:
type:
- string
- 'null'
maxLength: 1000
medium:
type:
- string
- 'null'
maxLength: 1000
source:
type:
- string
- 'null'
maxLength: 1000
term:
type:
- string
- 'null'
maxLength: 1000
additionalProperties: false
description: UTM parameters associated with this contact
integrations:
type: object
properties:
bitrixId:
type:
- integer
- 'null'
minimum: 1
maximum: 9007199254740991
hubspotId:
type:
- string
- 'null'
format: uuid
pdsId:
type:
- string
- 'null'
maxLength: 1000
photovateId:
type:
- string
- 'null'
maxLength: 1000
pipedriveId:
type:
- integer
- 'null'
minimum: 1
maximum: 9007199254740991
weclappId:
type:
- string
- 'null'
format: uuid
additionalProperties: false
description: IDs of this contact in third-party systems
required:
- firstName
- lastName
- marketingConsent
additionalProperties: false
securitySchemes:
X-Authorization:
type: apiKey
in: header
name: X-Authorization
x-tagGroups:
- name: People
tags:
- Contacts
- Users
- Teams
- name: Projects
tags:
- Residential Projects
- Commercial Projects
- name: Working on a project
tags:
- Notes
- Tasks
- Files
- File Folders
- Activities
- Time Tracking
- Checklists
- Checklist Templates
- Signature Requests
- name: Calendar
tags:
- Calendars
- Calendar Categories
- Appointments
- name: Catalog
tags:
- Components
- Planning Templates
- Planning Packages
- Offer Templates
- name: Workspace setup
tags:
- Kanban Boards
- Kanban Columns
- Tags
- Lead Sources
- name: Wiki
tags:
- Wiki
- name: Services
tags:
- Photogrammetry
- name: API helpers
tags:
- Upload
- Links
- name: Integrations
tags:
- Webhooks
- name: Guides
tags:
- Migrating from API v2 to v3
- Changelog