---
title: "Create profile"
method: POST
path: "/v1/profiles"
tags: ["Profiles"]
---

# Create profile

`POST /v1/profiles`

Creates a new profile with a name, optional description, and color. Names are unique per workspace: a duplicate returns a 409 whose details.existingProfileId carries the id of the existing profile. Send an Idempotency-Key header to make retries safe: a retried create with the same key and body replays the original 201 (same _id) instead of conflicting.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `name` string, required
  - `description` string
  - `color` string

## Response `201`

Created

- ProfileCreateResponse
  - `message` string
  - `profile` Profile
    - `_id` string
    - `userId` string
    - `name` string
    - `description` string
    - `color` string
    - `isDefault` boolean
    - `isOverLimit` boolean — Only present when includeOverLimit=true. Indicates if this profile exceeds the plan limit.
    - `createdAt` string, date-time

## Other responses

- `400` — Invalid request
- `401` — Unauthorized
- `402` — Payment method or enterprise contract required. The authenticated account hit a billing gate before the connection could proceed. Three reasons: - `free_tier_exceeded`: the team has connected more accounts than the free tier allows. Add a payment method on the dashboard to continue (the user will be billed per additional connected account). - `twitter_passthrough`: connecting an X (Twitter) account requires a card on file from day one because X API calls incur real per-call pass-through costs. Applies to the 1st X account, not just the 3rd+. - `enterprise_required`: the team is on an enterprise contract with a negotiated connected-account cap and has reached it. Self-service teams have NO connection cap (the $1/account rate continues at any scale), so this reason can only fire for teams whose contract sets an explicit limit. `dashboard_url` deep-links to the enterprise contact page rather than the billing tab. The end-user already has a card on file; this gate is about contract terms, not card collection. SDK consumers should switch on `reason` to render the right prompt. For `free_tier_exceeded` and `twitter_passthrough`, redirect the end-user to `dashboard_url` to add a payment method via Zernio's hosted Stripe Setup Checkout. For `enterprise_required`, redirect to `dashboard_url` (the enterprise contact form) to adjust the contract's limit.
- `403` — Profile limit exceeded
- `409` — A profile with this name already exists (code: profile_name_conflict); details.existingProfileId carries the id of the existing profile. Also returned while a request with the same Idempotency-Key is still processing.
- `422` — Idempotency-Key reused with a different body

---

[API](https://skmtc.net/zernio/apis/zernio-api.md) · [All operations](https://skmtc.net/zernio/apis/zernio-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/zernio/zernio-api/revisions/51932b099b2f/schema)
