Skip to content

Reference/REST API

REST API

Every project endpoint on https://api.usevelo.xyz, grouped by the resource it acts on. This page is generated from the OpenAPI document Velo publishes, so it cannot drift from the API it describes.

Conventions

Requests and responses are JSON. Bodies use snake_case. Timestamps are RFC 3339 in UTC. Creation returns 201, deletion returns 204, everything else returns 200. A request body is capped at 65536 bytes.

List endpoints take limit and offset, default 50, maximum 200, and answer with the collection alongside limit, offset and has_more.

curl
curl -G https://api.usevelo.xyz/v1/rooms \
  -H "Authorization: Bearer $VELO_API_KEY" \
  -d limit=50 \
  -d offset=0

Endpoints marked as a moderation call accept two credentials: a project API key, which always acts for the project, or a room token whose role carries the matching permission. Everything else needs the project API key. Unsafe calls accept an Idempotency-Key header; replaying the same key with a different body fails with 409 idempotency_key_reuse.

Every failure, at every status, uses one envelope. code is stable and safe to branch on, message is prose, and field names the request field or the missing permission when the failure is about one of them.

json404 Not Found
{
  "error": {
    "code": "room_not_found",
    "message": "room not found",
    "field": "name"
  }
}

See Authentication for which credential belongs where.

Call this API from your server, not from a browser. Velo answers cross-origin preflight only for the console itself, and does not accept an Authorization header cross-origin, so a request from your own web page is refused with 403 origin_rejected. This is deliberate: an API key in a browser is a public API key. Browsers get a room token, minted by your server, and use it with a client SDK.

Room names are used once

A room name belongs to one room for the life of the project. Creating a room with a name the project has used before returns 409 room_exists, whether or not that room is still running. Deleting a room ends it; the name is not released. Minting a token against a room that has ended returns 409 room_ended.

So a room name is a one-time identifier, not a stable address. Derive it from the thing that happens once, the consultation or the booking attempt, rather than from the thing that recurs, the customer or the weekly meeting. Store the name you generated and read it back; do not recompute it and hope it matches.

typescriptA name you can only use once, kept where you can find it
const name = `consult-${appointmentId}-${crypto.randomUUID().slice(0, 8)}`;

const room = await velo.rooms.create({ name, template_id: "tpl_consult" });
await db.appointments.update(appointmentId, { veloRoomName: room.name });

If two of your own workers might create the same room at once, make the write that stores the name the thing that decides the winner, and end the room the loser created. Two rooms both count against the plan's concurrent room ceiling until one of them ends.

Rate limits

Velo allows 20 requests a second with a burst of 40. The bucket is the API key, or the client address for calls that carry no key, so one project's traffic cannot slow another's. Going over returns 429 rate_limited with a Retry-After header in seconds. Honour it. Retrying immediately keeps the bucket empty and makes the outage last longer than it needed to.

Exchanging a room code is limited separately and far more tightly, to 1 a second with a burst of 5, per address, because that is the one route open to the public internet without a credential.

Nothing else is metered by request count. What a plan caps is concurrent rooms, people per room and participant minutes a month, and those refuse with 402, not 429.

Errors

Every code Velo can return, by status. code is stable and safe to branch on; message is prose and may be reworded. Treat a code you do not recognise as its status class.

400 Bad Request — the request itself is wrong

invalid_request
A field is missing, malformed, or outside its allowed set. field names it.
invalid_room_name
A room name must match [a-zA-Z0-9_-]{1,128}.
invalid_identity
A participant identity must match [a-zA-Z0-9_.@-]{1,128}.
invalid_url
A destination or endpoint URL is not a valid http(s) URL.
blocked_url
The URL resolves to a private or link-local address. Velo will not call it.
invalid_filepath
A recording filepath must be a relative name inside the project's recording directory.

401 Unauthorized — the credential is missing, malformed or revoked

unauthorized
No usable credential. Check the Authorization header and that the key is still live.

403 Forbidden — the credential is real but may not do this

permission_denied
A room token whose role lacks the permission this call needs. field names the missing permission.
origin_rejected
A browser tried to call the API. See Calling from a server, above.

402 Payment Required — the plan or the wallet will not carry it

room_limit_reached
The plan's concurrent room ceiling is already taken. End a room or move up a plan.
quota_exceeded
The month's participant minutes are spent, so rooms, tokens and recordings are refused until the period rolls over. Also returned when a project is suspended.
feature_not_in_plan
Recording or live streaming on a plan that does not include it, which is what Free is. field is recording or streaming.
insufficient_balance
The wallet cannot cover the call.

404 Not Found — no such thing, or not yours

room_not_found
No room by that name in this project.
session_not_found
No session by that id.
template_not_found
No template by that id, or the room resolves to no template.
role_not_found
No role by that name in this template.
destination_not_found
No destination by that name on this template.
recording_not_found
No recording by that egress id.
broadcast_not_found
No broadcast by that egress id.
ingress_not_found
No ingress by that id.
endpoint_not_found
No webhook endpoint by that id.
delivery_not_found
No webhook delivery by that id.
code_not_found
No room code by that value, or it has been revoked.

409 Conflict — the state will not allow it

room_exists
This project has used that room name before. Names are used once.
room_ended
The room has ended, so no new tokens can be minted for it. Create a new room.
idempotency_key_reuse
The same Idempotency-Key arrived with a different body.
recording_exists
That egress is already being recorded.
broadcast_exists
That egress is already being broadcast.
ingress_exists
That ingress is already registered.
template_exists
A template with that name already exists.
template_in_use
Rooms still reference this template.
template_is_default
The project's default template cannot be deleted.
destination_exists
A destination with that name already exists on this template.
destination_in_use
A recording or broadcast is still running against it. Stop that first.
role_capacity_reached
The role is full for this room.
role_referenced
Participants still hold this role, so it cannot be removed.
delivery_not_retryable
Only a delivery whose status is failed can be retried.

413 Payload Too Large

payload_too_large
The request body exceeds 65536 bytes.

429 Too Many Requests

rate_limited
Slow down and honour Retry-After. See Rate limits, above.

500 and 503 Ours, not yours — safe to retry with backoff

internal_error
An unhandled fault. Retry; if it persists, the status page will say so.
database_unavailable
Velo could not reach its database.
billing_unavailable
Velo could not reach billing, so it would not confirm the call is within plan.
destination_unusable
The stored credentials for this destination could not be decrypted. Re-save them.

The OpenAPI document

Velo publishes an OpenAPI 3.1.0 description of the whole project API at openapi.json. Every endpoint below is read out of that file at build time, including the paths, the request fields, the response fields and the credential each one takes. Point a generator at it and the client you get back is the API you are actually calling.

curl
curl -O https://usevelo.xyz/openapi.json

npx @redocly/cli lint openapi.json

The tables below list the fields of each request and each response one level deep. Where a field's type is a named object, such as Role or PublishProfile, its own fields are in the document under components.schemas.

The document describes 54 operations: 53 that need a project API key, of which 9 are moderation calls that a room token can also make, and 1 open route for exchanging a room code. The collection at Velo API.postman_collection.json carries 60 requests. It covers the features a project key can reach and nothing else: the admin routes, the billing service and the metrics endpoints are deliberately absent, because a project key cannot call them and publishing them would only describe a door you have no reason to knock on.

Rooms

A room is created before anyone joins and holds its name, capacity, metadata and template for the life of the call.

List rooms

GET/v1/rooms

Every room in the project, newest first, including rooms that have ended.

Project API key.

GET /v1/rooms parameters
FieldTypeRequiredDescription
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 RoomPage

A page of rooms.

GET /v1/rooms response 200
FieldTypeRequiredDescription
roomsRoom[]YesThe rooms in this page, newest first.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 429, 500. Every one carries the same error envelope.

Create a room

POST/v1/rooms

Creates the room before anyone joins. The room holds its name, capacity, metadata and template for the life of the call. Without template_id the project's default template is used, if it has one.

Project API key. Accepts an Idempotency-Key header.

Request body

POST /v1/rooms request body
FieldTypeRequiredDescription
namestringYesUnique within the project. Must match ^[a-zA-Z0-9_-]{1,128}$.
max_participantsintegerNo0 means the plan limit applies. Defaults to 0.
empty_timeout_secondsinteger or nullNoHow long the room survives with nobody in it. Defaults to 300.
metadataanyNoAny JSON. Returned unchanged on every read of the room.
template_idstringNoWhich template the room resolves roles against. Must match ^tpl_[0-9a-f]{12}$.

Response 201 Room

The room was created.

POST /v1/rooms response 201
FieldTypeRequiredDescription
iduuidYesVelo's internal identifier for the room row.
project_idstringYesThe project the room belongs to.
namestringYesThe name you created the room with.
namespaced_namestringYesThe name the media plane sees, which is the project id, a dot, then the room name.
max_participantsintegerYesThe cap requested at creation. Zero means the plan limit applies.
empty_timeout_secondsintegerYesHow long the room stays open with nobody in it. Defaults to 300.
metadataanyNoWhatever JSON you attached at creation, returned unchanged.
statusstringYesEither open or ended. An ended room cannot mint new tokens.
template_idstring or nullNoThe template the room resolves roles against, or null if the project has no default template.
created_attimestampYes
started_attimestampNoWhen the first participant joined. Absent until then.
ended_attimestampNoWhen the room finished. Absent while it is open.

Errors: 400, 401, 402, 404, 409, 413, 429, 500. Every one carries the same error envelope.

Read one room

GET/v1/rooms/{name}

Reads the room record held by the control plane. It does not call the media plane, so it answers for a room nobody has joined yet.

Project API key.

GET /v1/rooms/{name} parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.

Response 200 Room

The room.

GET /v1/rooms/{name} response 200
FieldTypeRequiredDescription
iduuidYesVelo's internal identifier for the room row.
project_idstringYesThe project the room belongs to.
namestringYesThe name you created the room with.
namespaced_namestringYesThe name the media plane sees, which is the project id, a dot, then the room name.
max_participantsintegerYesThe cap requested at creation. Zero means the plan limit applies.
empty_timeout_secondsintegerYesHow long the room stays open with nobody in it. Defaults to 300.
metadataanyNoWhatever JSON you attached at creation, returned unchanged.
statusstringYesEither open or ended. An ended room cannot mint new tokens.
template_idstring or nullNoThe template the room resolves roles against, or null if the project has no default template.
created_attimestampYes
started_attimestampNoWhen the first participant joined. Absent until then.
ended_attimestampNoWhen the room finished. Absent while it is open.

Errors: 401, 404, 429, 500. Every one carries the same error envelope.

End the room and disconnect everyone

DELETE/v1/rooms/{name}

Closes the room on the media plane and marks it ended. No further tokens can be minted for it, so start another call with a new room.

Moderation call. A project API key, or a room token whose role holds the matching permission.

DELETE /v1/rooms/{name} parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.

Response 204

Deleted. No body.

Errors: 400, 401, 403, 404, 429, 500, 503. Every one carries the same error envelope.

Participants and data

Who is in the room right now, and the calls that act on them.

List everyone currently connected

GET/v1/rooms/{name}/participants

Asks the media plane who is in the room right now, then pages the answer. Nobody in the room means an empty list, not a 404.

Project API key.

GET /v1/rooms/{name}/participants parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 ParticipantPage

A page of participants.

GET /v1/rooms/{name}/participants response 200
FieldTypeRequiredDescription
participantsParticipant[]YesEveryone currently connected.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 404, 429, 500, 503. Every one carries the same error envelope.

Read one participant and their tracks

GET/v1/rooms/{name}/participants/{identity}

Reads one participant's live state, including the attributes Velo set on them and every track they publish.

Project API key.

GET /v1/rooms/{name}/participants/{identity} parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
identitystring · pathYesThe participant identity carried by the token they joined with.

Response 200 Participant

The participant.

GET /v1/rooms/{name}/participants/{identity} response 200
FieldTypeRequiredDescription
sidstringYesThe participant identifier assigned by the media server for this join.
identitystringYesThe identity the token was minted for.
namestringYesThe display name carried by the token.
statestringYesjoining, joined, active or disconnected.
kindstringYesstandard, ingress, egress, sip or agent.
metadatastringYesThe metadata string carried by the token.
attributesmap of stringNoServer-set attributes, including velo.role and velo.publish.max_kbps.
is_publisherbooleanYes
joined_attimestampNo
tracksTrack[]YesEvery track this participant currently publishes.

Errors: 400, 401, 404, 429, 500, 503. Every one carries the same error envelope.

Remove a participant

DELETE/v1/rooms/{name}/participants/{identity}

Disconnects one participant. Their token remains valid, so they can rejoin unless you also disable whatever issued it.

Moderation call. A project API key, or a room token whose role holds the matching permission.

DELETE /v1/rooms/{name}/participants/{identity} parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
identitystring · pathYesThe participant identity carried by the token they joined with.

Response 204

Deleted. No body.

Errors: 400, 401, 403, 404, 429, 500, 503. Every one carries the same error envelope.

Mute or unmute one track

POST/v1/rooms/{name}/participants/{identity}/mute

Muting needs the mute_others permission and unmuting needs unmute_others, so a role can be allowed to quieten someone without being allowed to switch them back on.

Moderation call. A project API key, or a room token whose role holds the matching permission. Accepts an Idempotency-Key header.

POST /v1/rooms/{name}/participants/{identity}/mute parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
identitystring · pathYesThe participant identity carried by the token they joined with.

Request body

POST /v1/rooms/{name}/participants/{identity}/mute request body
FieldTypeRequiredDescription
track_sidstringYesThe track to act on, from the participant's tracks list.
mutedbooleanYesTrue to mute, false to unmute. There is no default.

Response 200 object

The track in its new state.

POST /v1/rooms/{name}/participants/{identity}/mute response 200
FieldTypeRequiredDescription
trackTrackYes

Errors: 400, 401, 403, 404, 413, 429, 500, 503. Every one carries the same error envelope.

Send a data payload into the room

POST/v1/rooms/{name}/send-data

Delivers an arbitrary payload to everyone in the room, or to named identities. The payload is opaque to Velo.

Moderation call. A project API key, or a room token whose role holds the matching permission. Accepts an Idempotency-Key header.

POST /v1/rooms/{name}/send-data parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.

Request body

POST /v1/rooms/{name}/send-data request body
FieldTypeRequiredDescription
datastringYesThe payload, standard base64.
kindstringNoDefaults to reliable. One of reliable, lossy.
destination_identitiesstring[]NoWho receives it. An empty list means everyone.
topicstringNoAn optional topic the client can filter on.

Response 200 object

The payload was handed to the media plane.

POST /v1/rooms/{name}/send-data response 200
FieldTypeRequiredDescription
sentbooleanYesAlways true when the call succeeds.
bytesintegerYesThe decoded payload size.

Errors: 400, 401, 403, 404, 413, 429, 500, 503. Every one carries the same error envelope.

Move a participant to another role mid-call

POST/v1/rooms/{name}/participants/{identity}/role

Rewrites the participant's grants and velo.role attribute in place, and the media server pushes a refreshed token to them. max_peer_count is not re-checked for someone already in the room. Emits participant.role_changed.

Moderation call. A project API key, or a room token whose role holds the matching permission. Accepts an Idempotency-Key header.

POST /v1/rooms/{name}/participants/{identity}/role parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
identitystring · pathYesThe participant identity carried by the token they joined with.

Request body

POST /v1/rooms/{name}/participants/{identity}/role request body
FieldTypeRequiredDescription
rolestringYesA role that exists in the room's template. Must match ^[a-zA-Z0-9_-]{1,64}$.

Response 200 RoleChangeResult

The change, the participant's new state, and a note on what is deferred.

POST /v1/rooms/{name}/participants/{identity}/role response 200
FieldTypeRequiredDescription
role_changeRoleChangeYes
participantParticipantYes
applies_livebooleanYesAlways true. Grants and the velo.role attribute reach the participant immediately.
notestringYesProse on what does not change until the participant republishes.

Errors: 400, 401, 403, 404, 413, 429, 500, 503. Every one carries the same error envelope.

Read the role change audit trail

GET/v1/rooms/{name}/role-changes

Every role change made in this room, newest first, with who made it.

Project API key.

GET /v1/rooms/{name}/role-changes parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
identitystring · queryNoOnly changes affecting this participant.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 RoleChangePage

A page of role changes.

GET /v1/rooms/{name}/role-changes response 200
FieldTypeRequiredDescription
role_changesRoleChange[]YesThe role changes for this room, newest first.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Tokens

One endpoint. It admits one identity to one room, optionally as a named role.

Mint a join token

POST/v1/tokens

Admits one identity to one room, optionally as a named role. Naming a role replaces permissions with the role's grants, attaches the velo.role and velo.publish.max_kbps attributes, and returns the publish profile. A room that has ended is refused.

Project API key. Accepts an Idempotency-Key header.

Request body

POST /v1/tokens request body
FieldTypeRequiredDescription
roomstringYesAn existing room in this project. Must match ^[a-zA-Z0-9_-]{1,128}$.
identitystringYesWho the token is for. Two live participants cannot share an identity. Must match ^[a-zA-Z0-9_.@-]{1,128}$.
namestringNoA display name carried into the room.
metadatastringNoAn opaque string handed to every other participant.
ttl_secondsintegerNoHow long the token is valid. 0 takes the server default.
rolestringNoA role in the room's template. Omit it to fall back to the permissions block. Must match ^[a-zA-Z0-9_-]{1,64}$.
permissionsobjectNoUsed only when no role is named. Each field defaults to true.

Response 201 Token

The token, the connection url and when it expires.

POST /v1/tokens response 201
FieldTypeRequiredDescription
tokenstringYesThe room token. Safe to hand to a browser.
urlstringYesThe websocket url the client should connect to.
expires_attimestampYes
rolestringNoThe role the token was minted for. Absent when no role was requested.
publish_profilePublishProfileNo

Errors: 400, 401, 402, 404, 409, 413, 429, 500, 503. Every one carries the same error envelope.

Templates

A reusable bundle of roles and settings that a room points at.

List templates

GET/v1/templates

Every template in the project, each with its roles.

Project API key.

GET /v1/templates parameters
FieldTypeRequiredDescription
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 TemplatePage

A page of templates.

GET /v1/templates response 200
FieldTypeRequiredDescription
templatesTemplate[]YesThe templates in this page.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 429, 500. Every one carries the same error envelope.

Create a template with its roles

POST/v1/templates

Creates a template and every role in one call. Each role is normalised against the documented defaults and then validated, so a role sent as an empty object is still complete.

Project API key. Accepts an Idempotency-Key header.

Request body

POST /v1/templates request body
FieldTypeRequiredDescription
namestringYesMust match ^[a-zA-Z0-9_-]{1,64}$.
settingsTemplateSettingsNo
rolesmap of RoleInputNoRoles keyed by name. Names that appear here can be referenced from subscribe.to_roles.

Response 201 Template

The template with its roles resolved.

POST /v1/templates response 201
FieldTypeRequiredDescription
idstringYesMust match ^tpl_[0-9a-f]{12}$.
project_idstringYes
namestringYesMust match ^[a-zA-Z0-9_-]{1,64}$.
is_defaultbooleanYesThe default template is used by rooms created without a template_id, and cannot be deleted.
settingsTemplateSettingsYes
rolesmap of RoleYesEvery role in the template, keyed by role name.
created_attimestampYes
updated_attimestampYes

Errors: 400, 401, 409, 413, 429, 500. Every one carries the same error envelope.

Read a template and every role in it

GET/v1/templates/{id}

Returns the template with its settings and the full, normalised body of every role.

Project API key.

GET /v1/templates/{id} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.

Response 200 Template

The template.

GET /v1/templates/{id} response 200
FieldTypeRequiredDescription
idstringYesMust match ^tpl_[0-9a-f]{12}$.
project_idstringYes
namestringYesMust match ^[a-zA-Z0-9_-]{1,64}$.
is_defaultbooleanYesThe default template is used by rooms created without a template_id, and cannot be deleted.
settingsTemplateSettingsYes
rolesmap of RoleYesEvery role in the template, keyed by role name.
created_attimestampYes
updated_attimestampYes

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Update the name, settings or named roles

PATCH/v1/templates/{id}

Only the fields you send are touched. Roles sent here are upserted whole; roles you leave out are untouched. Deleting a role needs the role endpoint.

Project API key.

PATCH /v1/templates/{id} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.

Request body

PATCH /v1/templates/{id} request body
FieldTypeRequiredDescription
namestring or nullNoMust match ^[a-zA-Z0-9_-]{1,64}$.
settingsTemplateSettingsNo
rolesmap of RoleInputNoRoles keyed by name, each replaced whole.

Response 200 Template

The template after the update.

PATCH /v1/templates/{id} response 200
FieldTypeRequiredDescription
idstringYesMust match ^tpl_[0-9a-f]{12}$.
project_idstringYes
namestringYesMust match ^[a-zA-Z0-9_-]{1,64}$.
is_defaultbooleanYesThe default template is used by rooms created without a template_id, and cannot be deleted.
settingsTemplateSettingsYes
rolesmap of RoleYesEvery role in the template, keyed by role name.
created_attimestampYes
updated_attimestampYes

Errors: 400, 401, 404, 409, 413, 429, 500. Every one carries the same error envelope.

Delete a template

DELETE/v1/templates/{id}

Refused with 409 if the template is the project default, or if any room still points at it.

Project API key.

DELETE /v1/templates/{id} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.

Response 204

Deleted. No body.

Errors: 400, 401, 404, 409, 429, 500. Every one carries the same error envelope.

Read the region setting and what is allowed

GET/v1/templates/{id}/settings

Returns the stored region together with every region the API accepts and a note on how far region is honoured today.

Project API key.

GET /v1/templates/{id}/settings parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.

Response 200 TemplateSettingsDetail

The settings.

GET /v1/templates/{id}/settings response 200
FieldTypeRequiredDescription
regionstringYesThe region stored on this template.
allowed_regionsstring[]YesEvery region the API will accept.
region_enforcedbooleanYesFalse while Velo runs a single media region.
region_notestringYesProse explaining what region does and does not do today.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Update the region setting

PATCH/v1/templates/{id}/settings

Region is stored and validated. Velo runs one media region today, so it does not yet change where traffic goes.

Project API key.

PATCH /v1/templates/{id}/settings parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.

Request body

PATCH /v1/templates/{id}/settings request body
FieldTypeRequiredDescription
regionstringYesDefaults to auto. One of auto, af-east, eu-west, us-east.

Response 200 TemplateSettingsDetail

The settings after the update.

PATCH /v1/templates/{id}/settings response 200
FieldTypeRequiredDescription
regionstringYesThe region stored on this template.
allowed_regionsstring[]YesEvery region the API will accept.
region_enforcedbooleanYesFalse while Velo runs a single media region.
region_notestringYesProse explaining what region does and does not do today.

Errors: 400, 401, 404, 413, 429, 500. Every one carries the same error envelope.

Roles

Roles live inside a template and can also be addressed one at a time.

Read one role

GET/v1/templates/{id}/roles/{name}

Returns one role from the template, fully resolved.

Project API key.

GET /v1/templates/{id}/roles/{name} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.
namestring · pathYesThe role name.

Response 200 Role

The role.

GET /v1/templates/{id}/roles/{name} response 200
FieldTypeRequiredDescription
namestringYesUnique within the template. Must match ^[a-zA-Z0-9_-]{1,64}$.
publishRolePublishYes
subscribeRoleSubscribeYes
permissionsRolePermissionsYes
priorityintegerYes1 is the most important. Defaults to 3.
max_peer_countintegerYes-1 admits nobody, 0 is unlimited, a positive number caps concurrent holders. Defaults to 0.
hiddenbooleanYesA hidden participant is not visible to others in the room.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Replace one role whole

PUT/v1/templates/{id}/roles/{name}

Creates the role if it is new and replaces it otherwise. The body is normalised against the defaults first, so anything you leave out reverts to its default rather than keeping the old value.

Project API key.

PUT /v1/templates/{id}/roles/{name} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.
namestring · pathYesThe role name.

Request body

PUT /v1/templates/{id}/roles/{name} request body
FieldTypeRequiredDescription
namestringNoIf sent it must match the key or path segment the role is filed under.
publishRolePublishNo
subscribeRoleSubscribeNo
permissionsRolePermissionsNo
priorityintegerNo
max_peer_countintegerNo
hiddenbooleanNo

Response 200 Role

The role was replaced.

PUT /v1/templates/{id}/roles/{name} response 200
FieldTypeRequiredDescription
namestringYesUnique within the template. Must match ^[a-zA-Z0-9_-]{1,64}$.
publishRolePublishYes
subscribeRoleSubscribeYes
permissionsRolePermissionsYes
priorityintegerYes1 is the most important. Defaults to 3.
max_peer_countintegerYes-1 admits nobody, 0 is unlimited, a positive number caps concurrent holders. Defaults to 0.
hiddenbooleanYesA hidden participant is not visible to others in the room.

Response 201 Role

The role was created.

PUT /v1/templates/{id}/roles/{name} response 201
FieldTypeRequiredDescription
namestringYesUnique within the template. Must match ^[a-zA-Z0-9_-]{1,64}$.
publishRolePublishYes
subscribeRoleSubscribeYes
permissionsRolePermissionsYes
priorityintegerYes1 is the most important. Defaults to 3.
max_peer_countintegerYes-1 admits nobody, 0 is unlimited, a positive number caps concurrent holders. Defaults to 0.
hiddenbooleanYesA hidden participant is not visible to others in the room.

Errors: 400, 401, 404, 413, 429, 500. Every one carries the same error envelope.

Delete one role

DELETE/v1/templates/{id}/roles/{name}

Refused with 409 if another role in the template lists this one in subscribe.to_roles. Remove the reference first.

Project API key.

DELETE /v1/templates/{id}/roles/{name} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.
namestring · pathYesThe role name.

Response 204

Deleted. No body.

Errors: 400, 401, 404, 409, 429, 500. Every one carries the same error envelope.

Template destinations

Named recording and streaming targets, configured once on a template and referenced by name when a recording or broadcast starts.

List destinations on a template

GET/v1/templates/{id}/destinations

Returns every destination on the template. This list is not paged.

Project API key.

GET /v1/templates/{id}/destinations parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.

Response 200 DestinationList

The destinations.

GET /v1/templates/{id}/destinations response 200
FieldTypeRequiredDescription
destinationsDestination[]YesEvery destination on the template.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Create a named destination

POST/v1/templates/{id}/destinations

Stores an upload target or a set of RTMP urls under a name. Credentials are encrypted at rest, and the call fails with 503 if the deployment has no encryption key. A recording destination needs a plan that includes recording, and an rtmp destination needs a plan that includes live streaming.

Project API key. Accepts an Idempotency-Key header.

POST /v1/templates/{id}/destinations parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.

Request body

POST /v1/templates/{id}/destinations request body
FieldTypeRequiredDescription
namestringYesHow a recording or broadcast will refer to it. Must match ^[a-zA-Z0-9_-]{1,64}$.
kindstringYesImmutable once created. One of recording, rtmp.
configRecordingDestinationConfig or RtmpDestinationConfigYesA recording config when kind is recording, an rtmp config when kind is rtmp.

Response 201 Destination

The destination, with secrets redacted.

POST /v1/templates/{id}/destinations response 201
FieldTypeRequiredDescription
idstringYesMust match ^dst_[0-9a-f]{12}$.
template_idstringYes
namestringYesUnique within the template. This is the name a recording or broadcast refers to. Must match ^[a-zA-Z0-9_-]{1,64}$.
kindstringYesImmutable once created. One of recording, rtmp.
configRecordingDestinationConfig or RtmpDestinationConfigYesA RecordingDestinationConfig when kind is recording, an RtmpDestinationConfig when kind is rtmp. Secrets are redacted.
created_attimestampYes
updated_attimestampYes

Errors: 400, 401, 402, 404, 409, 413, 429, 500, 503. Every one carries the same error envelope.

Read one destination, with credentials redacted

GET/v1/templates/{id}/destinations/{name}

The stored secret is never returned. An S3 secret reads back as ***, and an RTMP stream key path reads back as REDACTED.

Project API key.

GET /v1/templates/{id}/destinations/{name} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.
namestring · pathYesThe destination name.

Response 200 Destination

The destination.

GET /v1/templates/{id}/destinations/{name} response 200
FieldTypeRequiredDescription
idstringYesMust match ^dst_[0-9a-f]{12}$.
template_idstringYes
namestringYesUnique within the template. This is the name a recording or broadcast refers to. Must match ^[a-zA-Z0-9_-]{1,64}$.
kindstringYesImmutable once created. One of recording, rtmp.
configRecordingDestinationConfig or RtmpDestinationConfigYesA RecordingDestinationConfig when kind is recording, an RtmpDestinationConfig when kind is rtmp. Secrets are redacted.
created_attimestampYes
updated_attimestampYes

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Replace a destination config

PATCH/v1/templates/{id}/destinations/{name}

config is required and replaces the stored configuration whole. Leaving credentials out of an S3 config keeps the stored ones. kind cannot be changed. A destination whose stored kind is recording needs a plan that includes recording, and one whose kind is rtmp needs a plan that includes live streaming.

Project API key.

PATCH /v1/templates/{id}/destinations/{name} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.
namestring · pathYesThe destination name.

Request body

PATCH /v1/templates/{id}/destinations/{name} request body
FieldTypeRequiredDescription
kindstringNoIf sent it must equal the stored kind. One of recording, rtmp.
configRecordingDestinationConfig or RtmpDestinationConfigYesThe replacement configuration.

Response 200 Destination

The destination after the update.

PATCH /v1/templates/{id}/destinations/{name} response 200
FieldTypeRequiredDescription
idstringYesMust match ^dst_[0-9a-f]{12}$.
template_idstringYes
namestringYesUnique within the template. This is the name a recording or broadcast refers to. Must match ^[a-zA-Z0-9_-]{1,64}$.
kindstringYesImmutable once created. One of recording, rtmp.
configRecordingDestinationConfig or RtmpDestinationConfigYesA RecordingDestinationConfig when kind is recording, an RtmpDestinationConfig when kind is rtmp. Secrets are redacted.
created_attimestampYes
updated_attimestampYes

Errors: 400, 401, 402, 404, 413, 429, 500, 503. Every one carries the same error envelope.

Delete a destination

DELETE/v1/templates/{id}/destinations/{name}

Refused with 409 while a recording or broadcast is still running against it.

Project API key.

DELETE /v1/templates/{id}/destinations/{name} parameters
FieldTypeRequiredDescription
idstring · pathYesThe template id.
namestring · pathYesThe destination name.

Response 204

Deleted. No body.

Errors: 400, 401, 404, 409, 429, 500. Every one carries the same error envelope.

Room codes

A short, shareable string bound to one room and one role, for putting people into a call without running a token backend.

List codes for a room

GET/v1/rooms/{name}/codes

Every code issued for the room, including expired and disabled ones.

Project API key.

GET /v1/rooms/{name}/codes parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 RoomCodePage

A page of codes.

GET /v1/rooms/{name}/codes response 200
FieldTypeRequiredDescription
codesRoomCode[]YesThe codes issued for this room.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Create a code

POST/v1/rooms/{name}/codes

Issues a short string bound to one room and one role. Send either expires_at or ttl_seconds, never both. The code is returned in full here and nowhere else.

Project API key. Accepts an Idempotency-Key header.

POST /v1/rooms/{name}/codes parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.

Request body

POST /v1/rooms/{name}/codes request body
FieldTypeRequiredDescription
rolestringYesA role that exists in the room's template. Must match ^[a-zA-Z0-9_-]{1,64}$.
expires_attimestampNoBetween 60 seconds and 30 days from now.
ttl_secondsintegerNoAn alternative to expires_at. Defaults to 24 hours.
max_usesinteger or nullNoOmit for no cap.
identitystringNoLocks the code to one identity, ignoring whatever the holder sends. Must match ^[a-zA-Z0-9_.@-]{1,128}$.

Response 201 RoomCode

The code.

POST /v1/rooms/{name}/codes response 201
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYesThe room the code admits the holder to.
rolestringYesThe role the minted token carries.
codestringYesThe shareable string. This is the only place it is returned in full.
identitystring or nullNoWhen set, the code always mints for this identity and ignores the one the holder sends.
expires_attimestampYes
max_usesinteger or nullNoNull means no cap.
usesintegerYesHow many times the code has been exchanged.
disabledbooleanYes
created_attimestampYes
updated_attimestampYes

Errors: 400, 401, 404, 413, 429, 500. Every one carries the same error envelope.

Disable a code

DELETE/v1/codes/{code}

Takes the code out of use immediately. Tokens already minted from it keep working until they expire.

Project API key.

DELETE /v1/codes/{code} parameters
FieldTypeRequiredDescription
codestring · pathYesThe code string.

Response 204

Deleted. No body.

Errors: 401, 404, 429, 500. Every one carries the same error envelope.

Exchange a code for a room token

POST/v1/codes/{code}/exchange

The one route in the API that takes no credential, because the whole point of a code is that the holder has none. It is rate limited per code and per client address. A disabled, expired, exhausted or unknown code all fail identically with 404 code_not_found, so the code space cannot be probed.

No credential. This route is open and rate limited.

POST /v1/codes/{code}/exchange parameters
FieldTypeRequiredDescription
codestring · pathYesThe code string.

Request body

POST /v1/codes/{code}/exchange request body
FieldTypeRequiredDescription
identitystringNoWho to mint for. Ignored when the code is locked to an identity. Must match ^[a-zA-Z0-9_.@-]{1,128}$.

Response 201 Token

The same body as POST /v1/tokens, ready to hand straight to a client.

POST /v1/codes/{code}/exchange response 201
FieldTypeRequiredDescription
tokenstringYesThe room token. Safe to hand to a browser.
urlstringYesThe websocket url the client should connect to.
expires_attimestampYes
rolestringNoThe role the token was minted for. Absent when no role was requested.
publish_profilePublishProfileNo

Errors: 400, 402, 404, 409, 413, 429, 500, 503. Every one carries the same error envelope.

Recordings

Recording is started against a room and then tracked by its egress id.

List recordings for one room

GET/v1/rooms/{name}/recordings

Every recording started against this room, newest first.

Project API key.

GET /v1/rooms/{name}/recordings parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 RecordingPage

A page of recordings.

GET /v1/rooms/{name}/recordings response 200
FieldTypeRequiredDescription
recordingsRecording[]YesThe recordings in this page.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Start recording a room

POST/v1/rooms/{name}/recordings

Starts an egress against the room and returns the recording immediately, before the file exists. Poll the recording or listen for recording.ended. Without a destination the file lands in the project's local recording directory. Recording is not on every plan: a project whose plan does not include it is refused with 402 feature_not_in_plan, naming recording in field. Listing, reading and stopping recordings stay available on every plan.

Moderation call. A project API key, or a room token whose role holds the matching permission. Accepts an Idempotency-Key header.

POST /v1/rooms/{name}/recordings parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.

Request body

POST /v1/rooms/{name}/recordings request body
FieldTypeRequiredDescription
typestringYesWhat to record. One of room_composite, track, participant.
track_idstringNoRequired when type is track, rejected otherwise.
identitystringNoRequired when type is participant, rejected otherwise.
layoutstringNoroom_composite only. Defaults to speaker. One of speaker, grid.
audio_onlybooleanNoroom_composite only. Mutually exclusive with video_only.
video_onlybooleanNoroom_composite only.
filepathstringNoA relative name inside the project recording directory. Defaults to a timestamped .mp4.
destinationstringNoThe name of a recording destination on the room's template.

Response 201 Recording

The recording, in its starting state.

POST /v1/rooms/{name}/recordings response 201
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYes
namespaced_roomstringYes
egress_idstringYesThe handle every later call uses.
typestringYesOne of room_composite, track, participant.
statusstringYesOne of starting, active, ending, complete, failed, aborted, limit_reached.
filepathstringYesWhere the file is written, inside the project's recording directory.
destinationstringNoThe template destination the upload used, if one was named.
destination_idstringNoThe id of that destination.
started_attimestampYes
ended_attimestampNo
errorstringNoSet when the status is failed.
created_attimestampYes

Errors: 400, 401, 402, 403, 404, 409, 413, 429, 500, 503. Every one carries the same error envelope.

List recordings across the project

GET/v1/recordings

Every recording in the project, filterable by room, status and start time.

Project API key.

GET /v1/recordings parameters
FieldTypeRequiredDescription
roomstring · queryNoOnly recordings for this room.
statusstring · queryNoOne of starting, active, ending, complete, failed, aborted, limit_reached.
aftertimestamp · queryNoOnly rows started at or after this RFC 3339 timestamp.
beforetimestamp · queryNoOnly rows started before this RFC 3339 timestamp.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 RecordingPage

A page of recordings.

GET /v1/recordings response 200
FieldTypeRequiredDescription
recordingsRecording[]YesThe recordings in this page.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 429, 500. Every one carries the same error envelope.

Read one recording

GET/v1/recordings/{egress_id}

Reads the recording. While it is still running Velo asks the media plane for fresh status and writes it back, so the answer is current rather than cached.

Project API key.

GET /v1/recordings/{egress_id} parameters
FieldTypeRequiredDescription
egress_idstring · pathYesThe egress id returned when the recording or broadcast started.

Response 200 Recording

The recording.

GET /v1/recordings/{egress_id} response 200
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYes
namespaced_roomstringYes
egress_idstringYesThe handle every later call uses.
typestringYesOne of room_composite, track, participant.
statusstringYesOne of starting, active, ending, complete, failed, aborted, limit_reached.
filepathstringYesWhere the file is written, inside the project's recording directory.
destinationstringNoThe template destination the upload used, if one was named.
destination_idstringNoThe id of that destination.
started_attimestampYes
ended_attimestampNo
errorstringNoSet when the status is failed.
created_attimestampYes

Errors: 400, 401, 404, 429, 500, 503. Every one carries the same error envelope.

Stop a recording

POST/v1/recordings/{egress_id}/stop

Asks the media plane to end the egress. The status moves to ending at least, and the file is finalised shortly after.

Moderation call. A project API key, or a room token whose role holds the matching permission. Accepts an Idempotency-Key header.

POST /v1/recordings/{egress_id}/stop parameters
FieldTypeRequiredDescription
egress_idstring · pathYesThe egress id returned when the recording or broadcast started.

Response 200 Recording

The recording after the stop.

POST /v1/recordings/{egress_id}/stop response 200
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYes
namespaced_roomstringYes
egress_idstringYesThe handle every later call uses.
typestringYesOne of room_composite, track, participant.
statusstringYesOne of starting, active, ending, complete, failed, aborted, limit_reached.
filepathstringYesWhere the file is written, inside the project's recording directory.
destinationstringNoThe template destination the upload used, if one was named.
destination_idstringNoThe id of that destination.
started_attimestampYes
ended_attimestampNo
errorstringNoSet when the status is failed.
created_attimestampYes

Errors: 400, 401, 403, 404, 429, 500, 503. Every one carries the same error envelope.

Ingress

Publish into a room from OBS Studio or any RTMP or WHIP encoder. The stream joins as an ordinary participant.

Create an ingress and get its stream key

POST/v1/rooms/{name}/ingress

Gives you a publish url and a stream key for OBS Studio or any RTMP or WHIP encoder. The stream joins the room as an ordinary participant under the identity you name. The key is returned once and only a hash is kept. Live streaming is not on every plan: a project whose plan does not include it is refused with 402 feature_not_in_plan, naming streaming in field. Listing, reading and deleting an ingress stay available on every plan.

Project API key.

POST /v1/rooms/{name}/ingress parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.

Request body

POST /v1/rooms/{name}/ingress request body
FieldTypeRequiredDescription
input_typestringYesOne of rtmp, whip.
participant_identitystringYesThe identity the stream joins as. Must match ^[a-zA-Z0-9_.@-]{1,128}$.
participant_namestringNoA display name for the stream.
enable_transcodingboolean or nullNoLeave unset to take the media plane's default for the input type.

Response 201 IngressCreated

The ingress, including the stream key.

POST /v1/rooms/{name}/ingress response 201
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYes
namespaced_roomstringYes
ingress_idstringYes
input_typestringYesOne of rtmp, whip.
participant_identitystringYesThe identity the incoming stream joins the room as.
participant_namestringNo
urlstringYesThe publish url with the stream key replaced by REDACTED.
statusstringYesOne of inactive, buffering, publishing, error, complete, deleted.
errorstringNo
created_attimestampYes
updated_attimestampYes
deleted_attimestampNo
stream_keystringYesReturned once, on creation. Velo keeps only a hash of it, so it cannot be read back.

Errors: 400, 401, 402, 404, 409, 413, 429, 500, 503. Every one carries the same error envelope.

List ingresses

GET/v1/ingress

Every ingress in the project, filterable by room, status and input type.

Project API key.

GET /v1/ingress parameters
FieldTypeRequiredDescription
roomstring · queryNoOnly ingresses for this room.
statusstring · queryNoOne of inactive, buffering, publishing, error, complete, deleted.
input_typestring · queryNortmp or whip.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 IngressPage

A page of ingresses.

GET /v1/ingress response 200
FieldTypeRequiredDescription
ingressesIngress[]YesThe ingresses in this page.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 429, 500. Every one carries the same error envelope.

Read one ingress

GET/v1/ingress/{ingress_id}

Reads the ingress and, unless it has been deleted, refreshes its status from the media plane first.

Project API key.

GET /v1/ingress/{ingress_id} parameters
FieldTypeRequiredDescription
ingress_idstring · pathYesThe ingress id.

Response 200 Ingress

The ingress.

GET /v1/ingress/{ingress_id} response 200
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYes
namespaced_roomstringYes
ingress_idstringYes
input_typestringYesOne of rtmp, whip.
participant_identitystringYesThe identity the incoming stream joins the room as.
participant_namestringNo
urlstringYesThe publish url with the stream key replaced by REDACTED.
statusstringYesOne of inactive, buffering, publishing, error, complete, deleted.
errorstringNo
created_attimestampYes
updated_attimestampYes
deleted_attimestampNo

Errors: 400, 401, 404, 429, 500, 503. Every one carries the same error envelope.

Delete an ingress

DELETE/v1/ingress/{ingress_id}

Retires the endpoint and invalidates its stream key. The row stays with status deleted so history is not lost.

Project API key.

DELETE /v1/ingress/{ingress_id} parameters
FieldTypeRequiredDescription
ingress_idstring · pathYesThe ingress id.

Response 204

Deleted. No body.

Errors: 400, 401, 404, 429, 500, 503. Every one carries the same error envelope.

Broadcasts

Push a room out to RTMP destinations such as YouTube or Twitch.

Start a broadcast

POST/v1/rooms/{name}/broadcasts

Pushes the composited room out to RTMP destinations such as YouTube or Twitch. Send urls inline or name a template destination, never both. Stream keys are redacted everywhere they are read back. Live streaming is not on every plan: a project whose plan does not include it is refused with 402 feature_not_in_plan, naming streaming in field. Listing, reading and stopping broadcasts stay available on every plan.

Moderation call. A project API key, or a room token whose role holds the matching permission. Accepts an Idempotency-Key header.

POST /v1/rooms/{name}/broadcasts parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.

Request body

POST /v1/rooms/{name}/broadcasts request body
FieldTypeRequiredDescription
urlsstring[]NoBetween one and eight rtmp:// or rtmps:// urls. Required unless destination is given.
destinationstringNoThe name of an rtmp destination on the room's template.
layoutstringNoDefaults to grid. One of grid, speaker.
audio_onlybooleanNo

Response 201 Broadcast

The broadcast, in its starting state.

POST /v1/rooms/{name}/broadcasts response 201
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYes
namespaced_roomstringYes
egress_idstringYes
urlsstring[]YesThe destination urls with the stream key path replaced by REDACTED.
statusstringYesOne of starting, active, ending, complete, failed, aborted, limit_reached.
destinationstringNoThe template destination used, if one was named.
destination_idstringNo
started_attimestampYes
ended_attimestampNo
errorstringNo
created_attimestampYes

Errors: 400, 401, 402, 403, 404, 409, 413, 429, 500, 503. Every one carries the same error envelope.

List broadcasts

GET/v1/broadcasts

Every broadcast in the project, filterable by room, status and start time.

Project API key.

GET /v1/broadcasts parameters
FieldTypeRequiredDescription
roomstring · queryNoOnly broadcasts for this room.
statusstring · queryNoOne of starting, active, ending, complete, failed, aborted, limit_reached.
aftertimestamp · queryNoOnly rows started at or after this RFC 3339 timestamp.
beforetimestamp · queryNoOnly rows started before this RFC 3339 timestamp.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 BroadcastPage

A page of broadcasts.

GET /v1/broadcasts response 200
FieldTypeRequiredDescription
broadcastsBroadcast[]YesThe broadcasts in this page.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 429, 500. Every one carries the same error envelope.

Read one broadcast

GET/v1/broadcasts/{egress_id}

Reads the broadcast, refreshing its status from the media plane while it is still running.

Project API key.

GET /v1/broadcasts/{egress_id} parameters
FieldTypeRequiredDescription
egress_idstring · pathYesThe egress id returned when the recording or broadcast started.

Response 200 Broadcast

The broadcast.

GET /v1/broadcasts/{egress_id} response 200
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYes
namespaced_roomstringYes
egress_idstringYes
urlsstring[]YesThe destination urls with the stream key path replaced by REDACTED.
statusstringYesOne of starting, active, ending, complete, failed, aborted, limit_reached.
destinationstringNoThe template destination used, if one was named.
destination_idstringNo
started_attimestampYes
ended_attimestampNo
errorstringNo
created_attimestampYes

Errors: 400, 401, 404, 429, 500, 503. Every one carries the same error envelope.

Stop a broadcast

POST/v1/broadcasts/{egress_id}/stop

Ends the egress. The status moves to ending at least, and settles once the media plane confirms.

Moderation call. A project API key, or a room token whose role holds the matching permission. Accepts an Idempotency-Key header.

POST /v1/broadcasts/{egress_id}/stop parameters
FieldTypeRequiredDescription
egress_idstring · pathYesThe egress id returned when the recording or broadcast started.

Response 200 Broadcast

The broadcast after the stop.

POST /v1/broadcasts/{egress_id}/stop response 200
FieldTypeRequiredDescription
iduuidYes
project_idstringYes
roomstringYes
namespaced_roomstringYes
egress_idstringYes
urlsstring[]YesThe destination urls with the stream key path replaced by REDACTED.
statusstringYesOne of starting, active, ending, complete, failed, aborted, limit_reached.
destinationstringNoThe template destination used, if one was named.
destination_idstringNo
started_attimestampYes
ended_attimestampNo
errorstringNo
created_attimestampYes

Errors: 400, 401, 403, 404, 429, 500, 503. Every one carries the same error envelope.

Sessions

One row per participant per join, with the role they joined as and how long they stayed.

List sessions

GET/v1/sessions

One row per participant per join, with the role they joined as, how long they stayed and how many tracks they published.

Project API key.

GET /v1/sessions parameters
FieldTypeRequiredDescription
roomstring · queryNoOnly sessions in this room.
identitystring · queryNoOnly sessions for this participant identity.
rolestring · queryNoOnly sessions joined under this role.
activeboolean · queryNotrue for sessions still connected, false for finished ones.
aftertimestamp · queryNoOnly sessions joined at or after this RFC 3339 timestamp.
beforetimestamp · queryNoOnly sessions joined before this RFC 3339 timestamp.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 SessionPage

A page of sessions.

GET /v1/sessions response 200
FieldTypeRequiredDescription
sessionsSession[]YesThe sessions in this page.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 429, 500. Every one carries the same error envelope.

Read one session and the tracks it published

GET/v1/sessions/{id}

The session with every track the participant published, each with when it started and when it ended.

Project API key.

GET /v1/sessions/{id} parameters
FieldTypeRequiredDescription
iduuid · pathYesThe session id.

Response 200 SessionDetail

The session and its tracks.

GET /v1/sessions/{id} response 200
FieldTypeRequiredDescription
iduuidYesOne row per participant per join.
roomstringYes
participant_identitystringYes
participant_sidstringYes
rolestring or nullYesThe role the participant joined as, or null when the token carried none.
joined_attimestampYes
left_attimestampNoAbsent while the participant is still connected.
duration_secondsintegerYesMeasured to left_at, or to now while the session is open.
track_countintegerYesHow many tracks the participant published during the session.
namespaced_roomstringYes
tracksSessionTrack[]YesEvery track published during the session, in the order they started.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

List sessions for one room

GET/v1/rooms/{name}/sessions

The same filters as the project-wide list, scoped to one room.

Project API key.

GET /v1/rooms/{name}/sessions parameters
FieldTypeRequiredDescription
namestring · pathYesThe room name as it was created, without the project prefix.
identitystring · queryNoOnly sessions for this participant identity.
rolestring · queryNoOnly sessions joined under this role.
activeboolean · queryNotrue for sessions still connected, false for finished ones.
aftertimestamp · queryNoOnly sessions joined at or after this RFC 3339 timestamp.
beforetimestamp · queryNoOnly sessions joined before this RFC 3339 timestamp.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 SessionPage

A page of sessions.

GET /v1/rooms/{name}/sessions response 200
FieldTypeRequiredDescription
sessionsSession[]YesThe sessions in this page.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

Webhooks

Register receivers, then read every delivery attempt and re-queue the ones that failed.

List endpoints

GET/v1/webhook-endpoints

Every registered receiver. Secrets are not included, because Velo cannot show them again.

Project API key.

GET /v1/webhook-endpoints parameters
FieldTypeRequiredDescription
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 WebhookEndpointPage

A page of endpoints.

GET /v1/webhook-endpoints response 200
FieldTypeRequiredDescription
webhook_endpointsWebhookEndpoint[]YesThe endpoints registered on this project.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 429, 500. Every one carries the same error envelope.

Register an endpoint and receive its signing secret once

POST/v1/webhook-endpoints

Registers a receiver. The url is resolved and checked before it is stored, so an address on a private network is refused. The signing secret is in this response and nowhere else.

Project API key. Accepts an Idempotency-Key header.

Request body

POST /v1/webhook-endpoints request body
FieldTypeRequiredDescription
urlstringYesAn http or https url that resolves to a public address.

Response 201 WebhookEndpointCreated

The endpoint and its signing secret.

POST /v1/webhook-endpoints response 201
FieldTypeRequiredDescription
iduuidYes
urlstringYes
secretstringYesThe signing secret, returned once and never again. Store it before you close the response.
activebooleanYes
created_attimestampYes

Errors: 400, 401, 413, 429, 500. Every one carries the same error envelope.

Delete an endpoint

DELETE/v1/webhook-endpoints/{id}

Stops delivery to this receiver. Deliveries already queued for it are dropped.

Project API key.

DELETE /v1/webhook-endpoints/{id} parameters
FieldTypeRequiredDescription
iduuid · pathYesThe endpoint id.

Response 204

Deleted. No body.

Errors: 400, 401, 404, 429, 500. Every one carries the same error envelope.

List delivery attempts

GET/v1/webhook-deliveries

Every delivery Velo has queued for this project, newest first, with the exact payload, the attempt count and the last error. This is where you look when a receiver stopped hearing from Velo.

Project API key.

GET /v1/webhook-deliveries parameters
FieldTypeRequiredDescription
statusstring · queryNopending, delivered or failed.
event_typestring · queryNoAn event name such as recording.ended.
endpoint_iduuid · queryNoOnly deliveries for one endpoint.
limitinteger · queryNoPage size, between 1 and 200. Defaults to 50.
offsetinteger · queryNoHow many rows to skip. Defaults to 0.

Response 200 WebhookDeliveryPage

A page of deliveries.

GET /v1/webhook-deliveries response 200
FieldTypeRequiredDescription
webhook_deliveriesWebhookDelivery[]YesThe delivery attempts in this page, newest first.
limitintegerYesThe page size that was applied.
offsetintegerYesThe offset that was applied.
has_morebooleanYesTrue when a further page exists.

Errors: 400, 401, 429, 500. Every one carries the same error envelope.

Re-queue a failed delivery

POST/v1/webhook-deliveries/{id}/retry

Resets the delivery to pending with the attempt count back at zero, so the full retry schedule runs again. Only a delivery whose status is failed can be retried.

Project API key. Accepts an Idempotency-Key header.

POST /v1/webhook-deliveries/{id}/retry parameters
FieldTypeRequiredDescription
iduuid · pathYesThe delivery id.

Response 200 WebhookDelivery

The delivery, back in the queue.

POST /v1/webhook-deliveries/{id}/retry response 200
FieldTypeRequiredDescription
iduuidYes
endpoint_iduuidYes
endpoint_urlstringYes
event_typestringYesThe event name, such as participant.joined.
payloadanyYesThe exact JSON body that was posted, byte for byte the body the signature covers.
statusstringYesOne of pending, delivered, failed.
attemptsintegerYesHow many times Velo has posted this delivery.
next_attempt_attimestampNoPresent only while the status is pending.
delivered_attimestampNo
created_attimestampYes
last_errorstringYesThe transport or status error from the most recent attempt.
response_statusinteger or nullYesThe HTTP status the receiver returned, or null if the request never completed.

Errors: 400, 401, 404, 409, 429, 500. Every one carries the same error envelope.

Usage and plan

What the project has consumed and what its plan allows.

Participant minutes per day for a period

GET/v1/usage

Daily participant minutes and session counts, with the plan status attached. Defaults to the last 30 days.

Project API key.

GET /v1/usage parameters
FieldTypeRequiredDescription
fromstring · queryNoFirst day, YYYY-MM-DD. Defaults to 29 days before to.
tostring · queryNoLast day, YYYY-MM-DD. Defaults to today in UTC.

Response 200 Usage

Usage for the period.

GET /v1/usage response 200
FieldTypeRequiredDescription
daysUsageDay[]YesOne row per day in the requested range.
total_participant_minutesnumberYes
planPlanStatusYes

Errors: 400, 401, 429, 500, 503. Every one carries the same error envelope.

The plan, quota state and current limits

GET/v1/plan

What the project is on, what it has used this period and how many rooms are open right now. features is a sibling of limits and says whether the plan includes recording and live streaming.

Project API key.

Response 200 PlanStatus

The plan status.

GET /v1/plan response 200
FieldTypeRequiredDescription
plan_idstringYes
display_namestringYes
price_ugxintegerYesThe monthly price in Ugandan shillings.
limitsPlanLimitsYes
featuresPlanFeaturesYes
quota_statestringYesAn exceeded project has used its whole monthly participant-minute block; a suspended project has been stopped for other reasons. Both are refused with 402 on anything that would start work. One of active, suspended, exceeded.
period_startstringYesThe first day of the current billing period, YYYY-MM-DD.
participant_minutes_usednumberYes
participant_minutes_remainingnumberYes
active_roomsintegerYes

Errors: 401, 429, 500. Every one carries the same error envelope.

Was this page useful?

Edit this page on GitHub