# POST /v1/streams

Create a stream

Adds a stream to this server. A transactional or a broadcast stream is available to every account; an inbound stream receives mail at its own address, and a server has one at a time, and a test server none. Each sending stream has a suppression list of its own.

Takes a server key (`sk_…`, or `sk_test_…` on a test server) as a bearer token. TypeScript: `sendora.streams.create({ kind, name })`. Python: `sendora.streams.create(kind=..., name=...)`.

## Request body

- `kind` ("transactional" | "broadcast" | "inbound", required): transactional or broadcast for sending, open to every account; a server has one live inbound stream.
- `name` (string (at least 1, at most 100 characters), required): 1 to 100 characters.

Example:

```json
{
  "kind": "transactional",
  "name": "Notices"
}
```

## Responses

### 201 A stream of this server.

- `streamId` (string (uuid), required): What a send names as streamId.
- `kind` ("transactional" | "broadcast" | "inbound", required): transactional for mail the recipient expects; broadcast for mail to many; inbound for mail the server receives.
- `name` (string (at least 1, at most 100 characters), required): The name given at creation, unique within the server.
- `isDefault` (boolean, required): The stream a send without a streamId goes on; one per server, transactional.
- `createdAt` (string (date-time), required)
- `archivedAt` (string (date-time) or null, required): Set once archived; the stream then takes no new messages.
- `pausedAt` (string (date-time) or null, required): Set while Sendora has paused the stream after complaints; it takes no messages until support has resumed it.
- `inboundAddress` (string or null, required): The address of an inbound stream, its id on inbound.sendora.se; mail sent there is received on the stream. Null on every other kind.
- `contentRetentionDays` (integer (1 to 30) or null, required): How many days an inbound stream keeps received content, 1 to 30; null means the default of 30. Null on every other kind.

Example:

```json
{
  "streamId": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "kind": "transactional",
  "name": "Notices",
  "isDefault": false,
  "createdAt": "2026-09-16T12:00:00.000Z",
  "archivedAt": null,
  "pausedAt": null,
  "inboundAddress": null,
  "contentRetentionDays": null
}
```

## Errors

Every error answers `error`, the code, and `message`, a sentence for a person. A code that adds fields is shown in full below the table.

| Code | Status | Meaning |
| --- | --- | --- |
| `invalid_request` | 400 | The body, the query or a header does not match what the route takes. |
| `unauthorized` | 401 | The key is missing, malformed or revoked. |
| `wrong_token_kind` | 403 | The key is of the other kind: a server key (sk_) where an account key (ak_) is needed, or the reverse. The message names the kind the operation takes. |
| `account_not_active` | 403 | The account is closed, or not yet approved for a live server's sends or an inbound stream. |
| `stream_exists` | 409 | The server already has a stream with this name, archived or not. |
| `inbound_stream_exists` | 409 | A server has one inbound stream at a time; archive it to make another. |
| `stream_kind_not_allowed` | 422 | A test server has no inbound stream; receive on a live server. |

### `invalid_request`

- `issues` (array of object): One entry per invalid field; absent when a header is wrong.
  - `path` (string, required): The field, dotted, such as to.0.email; empty when the whole body is wrong.
  - `message` (string, required)

Example:

```json
{
  "error": "invalid_request",
  "message": "The request is invalid: to.0: Invalid email address",
  "issues": [
    {
      "path": "to.0",
      "message": "Invalid email address"
    }
  ]
}
```

### `stream_exists`

- `streamId` (string (uuid), required): The stream that already carries the name.

Example:

```json
{
  "error": "stream_exists",
  "message": "This server already has a stream with that name.",
  "streamId": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
```

### `inbound_stream_exists`

- `streamId` (string (uuid), required): The server's live inbound stream.

Example:

```json
{
  "error": "inbound_stream_exists",
  "message": "This server already has an inbound stream. Archive it to make another; mail to the old address is then refused.",
  "streamId": "7c9e6679-7425-40de-944b-e07fc1f90ae7"
}
```
