# API documentation

Create voice and text rooms from your app, then share the link.

[Get API key](https://poof.vc/developer)

### Agent instructions

Read the [Markdown API reference](https://poof.vc/docs.md) or [llms.txt](https://poof.vc/llms.txt) before integrating.

- Use `POST /rooms` with an operator-provided, server-side API key. Browser connection and moderation endpoints are internal.

- Share `url` with guests. Keep `hostUrl`  private for the intended host; do not claim it or join a room unless asked.

- Handle `error.code`, respect `Retry-After`, and do not automatically retry a timed-out creation request.

- Keep API keys and sign-in links out of browser code, shared URLs, and logs.

## Create a room

POST `https://api.poof.vc/rooms`

Creates a room for up to five people. Rooms expire 24 hours after creation. You don’t need to choose a host first.

### Headers

#### `Authorization`

Type: string · required

Your API key, sent as `Bearer poof_…`. Keep it on your server.

#### `Content-Type`

Type: string · required

Must be `application/json`.

### Body parameters

Send a JSON object, up to 4,096 bytes. Use `{}` for no parameters. Unknown fields are rejected.

#### `nickname`

Type: string · optional

Pre-fills the nickname on the join screen; the user can edit it. Maximum 24 characters after trimming whitespace. An empty string is omitted. The nickname is included in the returned links.

#### cURL

```bash
curl https://api.poof.vc/rooms \
  -H "Authorization: Bearer $POOF_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"nickname":"blue_mcgoo"}'
```

#### JavaScript

```javascript
const response = await fetch('https://api.poof.vc/rooms', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POOF_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ nickname: 'blue_mcgoo' }),
})

const room = await response.json()
if (!response.ok) throw new Error(room.error.message)
// Share room.url
```

## Response

Returns `201 Created` with a JSON object. All four fields are always present.

#### `code`

Type: string

The six-character room code users can enter to join.

#### `url`

Type: string · URI

The room link to share with guests. Includes the nickname if provided.

#### `hostUrl`

Type: string · URI

A private, one-time host link. The person who clicks **Join room** through this link claims lock and remove controls. Opening the link alone doesn’t claim it. Share this with only the intended host; the room also works without a host.

#### `expiresAt`

Type: string · date-time

When the room expires, in UTC ISO 8601 format. This is 24 hours after creation.

#### 201 Room created

```json
{
  "code": "ABC234",
  "url": "https://poof.vc/r/ABC234?nickname=blue_mcgoo",
  "hostUrl": "https://poof.vc/r/ABC234?nickname=blue_mcgoo#host=…",
  "expiresAt": "2026-10-08T12:00:00.000Z"
}
```

## Errors

Failed requests return a non-2xx status and an `error` object. Use the code in your application and the message to understand what went wrong.

#### `error.code`

Type: string

A stable error code from the list below.

#### `error.message`

Type: string

A description of the problem.

#### `error.requestId`

Type: string

The request’s identifier. Also returned in the `X-Request-Id` header, including on successful requests.

#### `error.details`

Type: object[] · optional

Field-level information for validation errors. 

### Field error properties

#### `field`

Type: string

The field name, or `body` for the whole request.

#### `code`

Type: string

`too_long`, `invalid_value`, or `unknown_field`.

#### `message`

Type: string

A description of the field’s validation problem.

#### 400 Validation error

```json
{
  "error": {
    "code": "validation_error",
    "message": "One or more fields are invalid.",
    "details": [{
      "field": "nickname",
      "code": "too_long",
      "message": "Nickname must be 24 characters or fewer."
    }],
    "requestId": "req_…"
  }
}
```

### Error codes

| Status | Code | Description |
| --- | --- | --- |
| 400 | `invalid_json` | The body is not valid JSON. Send a JSON object. |
| 400 | `validation_error` | A field has an invalid value or is not supported. Check error.details. |
| 401 | `missing_api_key` | Add your API key to the Authorization header. |
| 401 | `invalid_api_key` | The key is invalid, expired, or revoked. Use an active key. |
| 403 | `account_suspended` | Developer access has been disabled. |
| 404 | `endpoint_not_found` | The endpoint does not exist. Check the URL. |
| 405 | `method_not_allowed` | Use POST. The Allow header lists supported methods. |
| 413 | `payload_too_large` | The request body exceeds 4,096 bytes. Send a smaller body. |
| 415 | `unsupported_media_type` | Set Content-Type to application/json. |
| 429 | `rate_limit_exceeded` | Your account or IP has reached its limit. Wait for Retry-After seconds. |
| 500 | `internal_error` | An unexpected error occurred. Keep the request ID for troubleshooting. |
| 503 | `service_unavailable` | A required service is temporarily unavailable. |

**Retrying requests** Each successful request creates a new room. A timeout or lost response can still mean a room was created; don’t automatically retry an uncertain result.

## Rate limits

The default limit is **10 requests per minute per developer**, shared across all your API keys. Requests that fail body validation also use this quota.

A `429` response includes `Retry-After`: the number of seconds to wait. Rejected requests don’t extend the window. A separate IP limit also applies.

### Response headers

Account quota headers appear after your key and account are verified and the quota is checked.

#### `X-RateLimit-Limit`

Type: integer

Your account’s request allowance for the minute.

#### `X-RateLimit-Remaining`

Type: integer

Requests left in the current window.

#### `X-RateLimit-Reset`

Type: integer

When the window resets, as a Unix timestamp in seconds.

#### `Retry-After`

Type: integer

Seconds to wait before retrying after a `429`.

## Agent integration instructions

- The supported developer endpoint is POST https://api.poof.vc/rooms. Browser signaling, host-claim, and moderation endpoints are internal; do not use them as a developer API.
- A human signs in at https://poof.vc/developer to create a key. Obtain an API key from your operator and store it in a server environment variable or secret manager. Never request a magic link without the user's authorization, or place a key in browser code, URLs, output logs, or shared links.
- Send Authorization: Bearer <key> and Content-Type: application/json. Send {} or an optional nickname string. Do not invent additional parameters.
- On 201, share url with guests. hostUrl is a private one-time claim link; give it only to the intended host. Joining through it grants host controls. Merely visiting it does not claim it. A room can work without a host.
- Do not follow hostUrl yourself or join a room on a user's behalf unless asked. Do not crawl room codes or export room participants, messages, or credentials.
- Use error.code for programmatic handling. Respect Retry-After on 429. Do not automatically retry a timed-out creation request: a room may already exist and a retry creates another room.
- Treat unknown server failures as uncertain results. Retain only the safe requestId for troubleshooting. Do not fabricate a successful room URL.
