---
title: "Create a contact"
method: POST
path: "/v1/contacts"
tags: ["Contacts"]
---

# Create a contact

`POST /v1/contacts`

## Request

- Base URL: `https://api.postzen.dev`
- URL: `https://api.postzen.dev/v1/contacts`
- Auth: HTTP bearer

Requires read_write. Limited to 600 creates/hour/user. Creates a contact and optionally its first channel. Maximum 5000 contacts per user and 20 channels per contact. Unknown accounts return 404 accountNotFound; accounts in another profile or platform mismatches return 400 validation. Out-of-scope profiles return 403 forbidden.

## Request body

- ContactCreateRequest: Creates a contact and optionally its first channel. If accountId, platformIdentifier, or platform is supplied, both accountId and platformIdentifier are required. Maximum 5000 contacts per user, 20 channels per contact. Duplicate email within the profile or duplicate channel returns 409.
  - `profileId` string, required: Owning profile id; immutable and must be in the API key scope.
  - `name` string, required: Trimmed contact name, 1–200 characters.
  - `email` string: Trimmed lowercase email; unique within its profile.
  - `company` string: Company name, at most 200 characters.
  - `tags` string[]: At most 50 tags, trimmed and deduplicated case-insensitively, preserving original case. Send [] to clear.
  - `isSubscribed` boolean: Contact subscription state. False prevents comment automation sends.
  - `notes` string: Free-form notes, at most 5000 characters.
  - `customFields` object: At most 50 keys of 1–64 characters. Keys follow Convex record rules: printable ASCII, not starting with $ or _. Values are strings (at most 1000 characters), finite numbers, booleans, or null. PATCH replaces the entire object.
  - `accountId` string: Connected account id; must belong to the same profile and caller.
  - `platformIdentifier` string: Platform identity (IGSID, PSID, or handle), trimmed, 1–200 characters. Unique per connected account.
  - `displayIdentifier` string: Optional username or friendly handle, at most 200 characters. Without a channel it is ignored and a warning is returned.
  - `platform` 'x' | 'instagram' | 'tiktok' | 'linkedin' | 'facebook' | 'youtube' | 'threads' | 'pinterest' | 'bluesky' | 'telegram': Optional assertion of the account platform. Must equal account.platform; requires accountId and platformIdentifier.

## Response `201`

Create a contact.

- object
  - `message` string, required: Operation confirmation.
  - `contact` Contact, required: A profile-owned person. Maximum 5000 contacts per user and 20 channels per contact. Absent optional fields are returned as null. Message history is not backfilled.
    - `id` string, required: Contact id.
    - `profileId` string, required: Owning profile id; immutable and must be in the API key scope.
    - `name` string, required: Trimmed contact name, 1–200 characters.
    - `email` string, nullable, required: Trimmed lowercase email; unique within its profile.
    - `company` string, nullable, required: Company name, at most 200 characters.
    - `avatarUrl` string, uri, nullable, required: Public HTTPS avatar URL, canonicalized on PATCH; inbox avatars are stored as supplied by the platform.
    - `tags` string[], required: At most 50 tags, trimmed and deduplicated case-insensitively, preserving original case. Send [] to clear.
    - `isSubscribed` boolean, required: Contact subscription state. False prevents comment automation sends.
    - `isBlocked` boolean, required: Blocked contacts cannot receive comment automation sends.
    - `notes` string, nullable, required: Free-form notes, at most 5000 characters.
    - `customFields` object, nullable, required: At most 50 keys of 1–64 characters. Keys follow Convex record rules: printable ASCII, not starting with $ or _. Values are strings (at most 1000 characters), finite numbers, booleans, or null. PATCH replaces the entire object.
    - `messagesSentCount` integer, required: Messages sent by the connected account, including automation DMs. Counters start when Contacts ships; history is not backfilled.
    - `messagesReceivedCount` integer, required: Messages received from this person since Contacts ships; re-syncing stored messages does not increment this count.
    - `lastMessageSentAt` string, date-time, nullable, required: Latest counted outbound message time, or null.
    - `lastMessageReceivedAt` string, date-time, nullable, required: Latest counted inbound message time, or null.
    - `source` 'api' | 'inbox' | 'automation', required: How this contact first came to exist; informational and immutable.
    - `createdAt` string, date-time, required: Contact creation time.
    - `updatedAt` string, date-time, required: Latest contact update time.
  - `channel` ContactChannel: One platform identity on one connected account. At most 20 channels per contact; optional fields are present as null.
    - `id` string, required: Contact channel id.
    - `contactId` string, required: Contact owning this channel.
    - `accountId` string, required: Connected account id; must belong to the same profile and caller.
    - `platform` 'x' | 'instagram' | 'tiktok' | 'linkedin' | 'facebook' | 'youtube' | 'threads' | 'pinterest' | 'bluesky' | 'telegram', required: PostZen platform. Always resolved from the connected account.
    - `platformIdentifier` string, required: Platform identity (IGSID, PSID, or handle), trimmed, 1–200 characters. Unique per connected account.
    - `displayIdentifier` string, nullable, required: Optional username or friendly handle, at most 200 characters.
    - `isSubscribed` boolean, required: Channel opt-in. False prevents comment automation sends on this channel.
    - `conversationId` string, nullable, required: Platform conversation id when known; not the PostZen inbox document id.
    - `metadata` object, nullable, required: Optional platform facts; same key and scalar value limits as customFields.
    - `lastActiveAt` string, date-time, nullable, required: Latest counted message time on this channel.
    - `createdAt` string, date-time, required: Channel creation time.
  - `warning` string: Included only when displayIdentifier was supplied without a channel.

## Other responses

- `400`: Invalid request.
- `401`: Missing or invalid API key.
- `403`: The API key does not have sufficient permission or profile access.
- `404`: Resource not found.
- `409`: duplicateContact: email exists in this profile; duplicateChannel: identity exists on this account; limitReached: 5000 contacts per user.
- `429`: Rate limit exceeded. Retry after the interval indicated by the `Retry-After` header.
- `500`: Unexpected server error.

---

[API](https://skmtc.dev/postzen/apis/postzen-public-api.md) · [All operations](https://skmtc.dev/postzen/apis/postzen-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/postzen/apis/postzen-public-api/revisions/40863e3710ec?raw)
