Skip to navigation

Workspaces

The sending tenant every message belongs to

A workspace is a sending tenant inside your account. It has its own message history, its own API keys, and the sending-IP pool its mail goes out through. Every message, delivery and event belongs to exactly one workspace, and a read never crosses that boundary: an id from another workspace is a 404, not a 403.

The default workspace

Every account starts with one workspace named default. If you never create another, you never have to think about workspaces again — a workspace key carries its own and the API routes everything there.

When to create more

Create a workspace when you want a separate history and separate keys:

  • Per environment — production and staging, so a test run never shows up in the numbers you report on.
  • Per product — one per application, so each team reads only its own traffic.
  • Per tenant — if you resell sending, one workspace per customer of yours keeps volumes and limits separate.

Do not create one per campaign or per message type. Filtering GET /v1/messages by status, recipient and created_at is the cheaper way to slice traffic you want kept together.

curl -X POST https://api.rray.app/v1/workspaces \
-H "Authorization: Bearer $RRAY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "production"}'
{
"id": "ws_01KYA3AWSZGCB9YMNKM5AQBA8G",
"object": "workspace",
"name": "production",
"status": "active",
"egress_pool": "default",
"monthly_delivery_limit": null,
"customer_id": "cus_01KYA3AWSYX8ZWK2CJ4N0JYQ7D",
"created_at": "2026-10-07T07:48:02.607020Z"
}

Names are 1 to 100 characters and unique within your account; a repeat is 409 workspace.name_taken. List what you have with GET /v1/workspaces — it is cursor-paginated like any other list.

The returned id is the value you send as RRay-Workspace-Id. monthly_delivery_limit is null until you cap the workspace in the dashboard, at which point that workspace is bounded by its own ceiling as well as the account’s.

Naming the workspace on a request

A workspace key already names its workspace, so it sends no header at all. A platform key acts for the whole account and therefore names the workspace on every operation that acts in one.

OperationWorkspace comes from
Send, list, read a message or its bodyThe workspace key, or RRay-Workspace-Id with a platform key
GET /v1/statsThe workspace key, or RRay-Workspace-Id / RRay-Workspace-Ids with a platform key
Create or list workspacesThe account — no header
GET /v1/usage, GET /v1/billingThe account — no header. A workspace key sees only its own workspace in the breakdown

Sending RRay-Workspace-Id with a workspace key is 400 validation.workspace_header_invalid, even when the value matches. See Credentials for the full header table.

Reading across workspaces

GET /v1/stats is the one endpoint that spans several workspaces. A platform key may send RRay-Workspace-Ids there instead of the singular header: 1 to 50 comma-separated ids.

curl "https://api.rray.app/v1/stats?group_by=day&last=7" \
-H "Authorization: Bearer $RRAY_PLATFORM_KEY" \
-H "RRay-Workspace-Ids: ws_01KYA3AWSZGCB9YMNKM5AQBA8G,ws_01KYA3AX0QV6N8XQ8E8G3RZ7MC"

Three rules hold:

  1. Never both headers. The singular and the plural together is always 400 validation.workspace_header_conflict.
  2. The plural header is GET /v1/stats only. Anywhere else it is 400 validation.workspace_header_invalid — a single-workspace operation will not silently pick one id out of a list.
  3. One bad id fails the whole request. A foreign or unknown id returns 404 workspace.not_found instead of being dropped, so a typo can never read as “this workspace had no traffic”.

Ids must be unique within the list, and the list is capped at 50.

Limits per workspace

A workspace can carry its own monthly_delivery_limit on top of the account allowance. When it does, a workspace key sending from it sees workspace.monthly in the RRay-Quota-* headers instead of the account totals, and a send beyond the ceiling is 403 billing.workspace_limit_exceeded. The account’s own ceilings still apply underneath — see Limits & facts.