# Profiles

Create, find, update and delete profiles, use externalId for your own ids, set a default profile and timezone, and move accounts between profiles.

A profile groups connected accounts under one brand, client or customer. Every workspace starts with a default profile. Add as many more as you need: profiles are free, and only connected accounts are billed.

## Create a profile

`name` is required. Everything else is optional.

```bash cURL
curl -X POST https://api.pozy.ai/v1/profiles \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Northwind Coffee",
    "description": "Retail client, three locations",
    "color": "#7C3AED",
    "externalId": "client_204"
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/profiles", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    name: "Northwind Coffee",
    description: "Retail client, three locations",
    color: "#7C3AED",
    externalId: "client_204",
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.profile._id);
```
```python Python
import os, requests

resp = requests.post(
    "https://api.pozy.ai/v1/profiles",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    json={
        "name": "Northwind Coffee",
        "description": "Retail client, three locations",
        "color": "#7C3AED",
        "externalId": "client_204",
    },
)
print(resp.json()["profile"]["_id"])
```

```json
{
  "message": "Profile created successfully",
  "profile": {
    "_id": "6722b8e05d1f4a97c3e2b018",
    "name": "Northwind Coffee",
    "description": "Retail client, three locations",
    "color": "#7C3AED",
    "timezone": null,
    "externalId": "client_204",
    "isDefault": false,
    "accountCount": 0,
    "createdAt": "2026-10-30T11:20:05.000Z"
  }
}
```

| Field | Notes |
| --- | --- |
| `name` | Required. Shown in the dashboard and in connect screens you brand. |
| `description` | Free text for your team. |
| `color` | Hex colour used to tell profiles apart in the dashboard. |
| `externalId` | Your own id for this profile, up to 255 characters. Unique in the workspace. |
| `isDefault` | Set `true` to make this the workspace's default profile. |

## Use externalId

`externalId` is how you tie a Pozy profile to a record in your system: a customer id, a tenant slug, a client code. It pays off in three places:

- **Lookups.** `GET /v1/profiles?externalId=client_204` returns that profile, so you never have to store the Pozy id if you prefer not to.
- **Retries.** A second create with the same `externalId` returns `409` with `code: "profile_external_id_conflict"` and `details.existingProfileId`. Treat it as "already done" and carry on with that id.
- **Webhooks.** Every event lists the profiles it concerns as `profiles: [{ "id", "externalId" }]`, so your handler can route without a database query ([route webhooks](https://pozy.ai/docs/build/route-webhooks)).

```json
{
  "error": "A profile with this externalId already exists.",
  "type": "invalid_request_error",
  "code": "profile_external_id_conflict",
  "details": { "existingProfileId": "6722b8e05d1f4a97c3e2b018" }
}
```

You can set, change or clear `externalId` later with an update. Clearing it (`null`) frees the value for another profile.

## List and find profiles

`GET /v1/profiles` returns every profile in the workspace, the default first. Filter with `name` or `externalId` (both exact matches).

```bash cURL
curl "https://api.pozy.ai/v1/profiles?externalId=client_204" \
  -H "Authorization: Bearer $POZY_API_KEY"
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/profiles?externalId=client_204", {
  headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
const profile = data.profiles[0]; // undefined when there is no match
```
```python Python
resp = requests.get(
    "https://api.pozy.ai/v1/profiles",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    params={"externalId": "client_204"},
)
profiles = resp.json()["profiles"]
```

A profile-scoped API key only ever sees the profiles it was given.

## Update a profile

`PUT /v1/profiles/{profileId}` changes only the fields you send. It also takes `timezone`, an IANA name such as `Europe/Vienna`, stored as the profile's default timezone.

```bash Terminal
curl -X PUT https://api.pozy.ai/v1/profiles/6722b8e05d1f4a97c3e2b018 \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Northwind Coffee Co.", "timezone": "Europe/Vienna" }'
```

Send `"isDefault": true` to make a profile the default. The previous default loses the flag.

## The default profile

There is always exactly one default profile, flagged `isDefault: true` and listed first. The dashboard opens on it. If you delete it, another profile becomes the default.

Keep the default for your own brand's accounts and give each customer their own profile next to it.

## Move an account to another profile

Accounts can change profile without reconnecting, for example when two clients merge or you reorganise brands. Send the target `profileId` to `PATCH /v1/accounts/{accountId}`:

```bash cURL
curl -X PATCH https://api.pozy.ai/v1/accounts/6720f41a8d3be96c1e5a02f7 \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "profileId": "6722b8e05d1f4a97c3e2b018" }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/accounts/6720f41a8d3be96c1e5a02f7", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ profileId: "6722b8e05d1f4a97c3e2b018" }),
});
if (!res.ok) throw new Error((await res.json()).error);
```
```python Python
requests.patch(
    "https://api.pozy.ai/v1/accounts/6720f41a8d3be96c1e5a02f7",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    json={"profileId": "6722b8e05d1f4a97c3e2b018"},
)
```

With a profile-scoped key, the target profile must be in the key's scope.

## Delete a profile

```bash Terminal
curl -X DELETE https://api.pozy.ai/v1/profiles/6722b8e05d1f4a97c3e2b018 \
  -H "Authorization: Bearer $POZY_API_KEY"
```

- A profile that still has active connected accounts cannot be deleted. Disconnect them first with `DELETE /v1/accounts/{accountId}`.
- Disconnected accounts and WhatsApp numbers still in the profile move to another of your profiles. They are never deleted along with it.
- Deleting is permanent. The `externalId` becomes free for reuse.
- API keys scoped to only this profile stop being useful. Revoke them ([tenant keys](https://pozy.ai/docs/build/tenant-keys)).

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/profiles`](https://pozy.ai/docs/api/listProfiles) | List profiles |
| [`POST /v1/profiles`](https://pozy.ai/docs/api/createProfile) | Create profile |
| [`GET /v1/profiles/{profileId}`](https://pozy.ai/docs/api/getProfile) | Get profile |
| [`PUT /v1/profiles/{profileId}`](https://pozy.ai/docs/api/updateProfile) | Update profile |
| [`DELETE /v1/profiles/{profileId}`](https://pozy.ai/docs/api/deleteProfile) | Delete profile |
