Skip to content

Calls

The Calls API manages call links, session lifecycle, call history, and scheduling. Call links are managed under /v1/calls/links/, sessions under /v1/calls/, and scheduling under /v1/schedule/.

WAITING --> RINGING --> ACTIVE --> COMPLETED
| |
+-- MISSED (host didn't |
join within 5 min) FAILED (billing error)
StateDescription
waitingGuest joined; hold placed; waiting for host
ringingHost notified of incoming call
activeBoth peers connected, timer running
completedSession finished, billing settled
missedHost did not join within the no-show threshold (5 minutes)
failedBilling error prevented completion

Create a new call link. Only hosts can create links.

Authentication: Bearer token with host or admin role.

POST /v1/calls/links
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Content-Type: application/json
{
"rate_per_minute": "2.50",
"window_size_minutes": 10,
"expires_at": "2026-03-22T00:00:00Z",
"max_uses": 1,
"label": "Session with Alex"
}
FieldTypeRequiredDescription
rate_per_minutestringYesPer-minute charge in USD. Min $0.50, max $999.99.
window_size_minutesintegerYesBilling window size (1—60 minutes)
expires_atstring (ISO 8601)NoWhen the link expires. Null = never.
max_usesintegerNoMax uses. Null = unlimited.
labelstringNoInternal label. Max 120 characters.
HTTP/1.1 201 Created
{
"id": "01JN2X7N1Q6I0TSCUYZ9AELGHI",
"short_code": "abc123",
"url": "https://vidivo.app/c/abc123",
"host_id": "01JN2X4K8M3F7QPZRVWT6YBHCE",
"rate_per_minute": "2.50",
"window_size_minutes": 10,
"expires_at": "2026-03-22T00:00:00Z",
"max_uses": 1,
"use_count": 0,
"label": "Session with Alex",
"is_active": true,
"created_at": "2026-03-15T10:00:00Z"
}
CodeStatusDescription
forbidden403User is not a host
validation_error400Invalid rate or window size

List all call links for the authenticated host.

Authentication: Bearer token with host or admin role.

GET /v1/calls/links
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...

Returns an array of call link objects.


Deactivate a call link. The link is soft-deleted and cannot be used for new calls.

Authentication: Bearer token with host or admin role. Must own the link.

DELETE /v1/calls/links/01JN2X7N1Q6I0TSCUYZ9AELGHI
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...

Returns 204 No Content on success.


Retrieve public link information for the guest join page. No authentication required.

GET /v1/calls/links/abc123/info
{
"short_code": "abc123",
"host": {
"display_name": "Jane Smith",
"avatar_url": "https://cdn.vidivo.app/avatars/01JN2X4K.jpg",
"username": "janecoach"
},
"rate_per_minute": "2.50",
"window_size_minutes": 10,
"is_active": true
}

Get the host’s most recent active call link by username. Public endpoint.

GET /v1/calls/links/active/janecoach

Join a call via a link. This is called by the guest (or authenticated user) to initiate a call session. The endpoint validates the link, creates a call session, and optionally places a Stripe hold for the first billing window.

Authentication: Guest token or user access token.

POST /v1/calls/join/abc123
Content-Type: application/json
X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{
"payment_method_id": "pm_1OqBuv2eZvKYlo2C8XXXXXXXXXXX",
"guest_name": "Alex",
"guest_email": "alex@example.com"
}
FieldTypeRequiredDescription
payment_method_idstringYesStripe PaymentMethod ID from Stripe Elements
guest_namestringNoDisplay name for the guest
guest_emailstringNoGuest email for receipt
HTTP/1.1 201 Created
{
"call": {
"id": "01JN2X6M0P5H9SRBTXY8ZDKEFG",
"status": "waiting",
"short_code": "abc123",
"host": {
"id": "01JN2X4K8M3F7QPZRVWT6YBHCE",
"display_name": "Jane Smith"
},
"rate_per_minute": "2.50",
"window_size_minutes": 10,
"created_at": "2026-03-15T14:00:00Z"
},
"signaling_token": "st_01JN2X6M0P5H9SRBTXY8ZDKEFG",
"turn_credentials": {
"urls": ["turn:turn.vidivo.app:3478", "turns:turn.vidivo.app:5349"],
"username": "1710090000:01JN2X6M0P5H9SRBTXY8ZDKEFG",
"credential": "hmac-sha1-credential-here"
}
}
CodeStatusDescription
not_found404Short code does not exist or link expired
conflict409Link has reached its maximum use count
stripe_error402Stripe hold could not be placed

The host joins their own call session after a guest has initiated it.

Authentication: Bearer token required. Must be the call’s host.

POST /v1/calls/01JN2X6M0P5H9SRBTXY8ZDKEFG/host-join
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
HTTP/1.1 201 Created
{
"call": {
"id": "01JN2X6M0P5H9SRBTXY8ZDKEFG",
"status": "active"
},
"signaling_token": "st_host_01JN2X6M0P5H9SRBTXY8ZDKEFG",
"turn_credentials": {
"urls": ["turn:turn.vidivo.app:3478", "turns:turn.vidivo.app:5349"],
"username": "1710090000:01JN2X4K8M3F7QPZRVWT6YBHCE",
"credential": "hmac-sha1-credential-here"
}
}

Join a scheduled call. Both host and guest use this endpoint to join a previously booked call.

Authentication: Bearer token required.

POST /v1/calls/01JN2X6M0P5H9SRBTXY8ZDKEFG/join
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...

End an active or waiting call session. Either the host or guest may call this endpoint.

Authentication: Bearer token required.

POST /v1/calls/01JN2X6M0P5H9SRBTXY8ZDKEFG/end
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Content-Type: application/json
X-Idempotency-Key: 550e8400-e29b-41d4-a716-446655440001
{
"reason": "user_ended"
}
FieldTypeRequiredDescription
reasonstringNouser_ended, host_ended, timeout, billing_error
{
"call": {
"id": "01JN2X6M0P5H9SRBTXY8ZDKEFG",
"status": "completed",
"duration_seconds": 450,
"billed_minutes": 8,
"total_charged": "16.00",
"currency": "usd",
"ended_at": "2026-03-15T14:07:30Z"
}
}
CodeStatusDescription
not_found404Call session not found
forbidden403Caller is not a participant in this call
conflict409Call is not in an endable state

Retrieve call session details.

Authentication: Bearer token required.

GET /v1/calls/01JN2X6M0P5H9SRBTXY8ZDKEFG
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...

Retrieve call history for the authenticated user. Supports filtering by status.

Authentication: Bearer token required.

GET /v1/calls/history?page=1&per_page=20&status=completed
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Query ParameterTypeDescription
pageintegerPage number (default: 1)
per_pageintegerResults per page (default: 20)
statusstringFilter by status: waiting, ringing, active, completed, missed, failed

Get a host’s available time slots for a date range. Public endpoint.

GET /v1/schedule/janecoach?start_date=2026-03-15&end_date=2026-03-22
Query ParameterTypeRequiredDescription
start_datestringYesStart date (YYYY-MM-DD)
end_datestringYesEnd date (YYYY-MM-DD)
{
"host": {
"display_name": "Jane Smith",
"username": "janecoach",
"rate_per_minute": "2.50",
"window_size_minutes": 10
},
"slots": [
{
"date": "2026-03-15",
"start_time": "09:00",
"end_time": "10:00",
"available": true
},
{
"date": "2026-03-15",
"start_time": "10:00",
"end_time": "11:00",
"available": false
}
]
}

Book a scheduled call with a host.

Authentication: Bearer token required.

POST /v1/schedule/book
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Content-Type: application/json
{
"host_username": "janecoach",
"scheduled_at": "2026-03-16T14:00:00Z",
"duration_minutes": 30,
"note": "Career coaching session"
}
CodeStatusDescription
not_found404Host not found
conflict409Time slot already booked
validation_error400Time is outside host’s availability

List the authenticated user’s scheduled calls.

Authentication: Bearer token required.

GET /v1/schedule/mine?page=1&per_page=20
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...

Cancel a scheduled call. Both host and guest can cancel.

Authentication: Bearer token required.

PUT /v1/schedule/01JN2X6M0P5H9SRBTXY8ZDKEFG/cancel
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...
Content-Type: application/json
{
"reason": "Schedule conflict"
}

Returns 204 No Content on success.


Download an .ics calendar invite file for a scheduled call.

Authentication: Bearer token required.

GET /v1/schedule/01JN2X6M0P5H9SRBTXY8ZDKEFG/calendar
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...

Returns an text/calendar file download with Content-Disposition: attachment; filename=vidivo-call.ics.


WebRTC signaling is handled over a WebSocket connection separate from the REST API.

Endpoint: wss://signal.vidivo.app/signal/ws?token=<signaling_token>

Obtain the signaling_token from POST /v1/calls/join/:short_code or POST /v1/calls/:id/host-join. Tokens are valid for 5 minutes.

All WebSocket messages use JSON:

{
"type": "offer | answer | ice_candidate | peer_joined | peer_left | error",
"payload": { ... }
}
Guest Signal Server Host
| | |
|-- connect(token) ------->| |
| |<-- connect(token) -----|
| | |
|<--- peer_joined ----------|---- peer_joined ------>|
| | |
|---- offer (SDP) -------->|---- offer (SDP) ------>|
| |<--- answer (SDP) ------|
|<--- answer (SDP) --------| |
| | |
|-- ice_candidate -------->|---- ice_candidate ---->|
|<- ice_candidate ---------|<--- ice_candidate -----|
| | |
|============ P2P WebRTC established ===============|
TypeDirectionDescription
offerGuest to HostWebRTC SDP offer
answerHost to GuestWebRTC SDP answer
ice_candidateBothICE candidate for NAT traversal
peer_joinedServer to BothOther participant connected
peer_leftServer to BothOther participant disconnected
errorServer to ClientSignaling error

Terminal window
# Create a call link
curl -X POST https://api.vidivo.app/v1/calls/links \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{
"rate_per_minute": "2.50",
"window_size_minutes": 10,
"max_uses": 1,
"label": "Session with Alex"
}'
# Join a call via link
curl -X POST https://api.vidivo.app/v1/calls/join/abc123 \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{
"payment_method_id": "pm_1OqBuv2eZvKYlo2C8XXXXXXXXXXX",
"guest_name": "Alex"
}'
# Get call history
curl "https://api.vidivo.app/v1/calls/history?status=completed" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..."
# End a call
curl -X POST https://api.vidivo.app/v1/calls/01JN2X6M0P5H9SRBTXY8ZDKEFG/end \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: $(uuidgen)" \
-d '{"reason":"user_ended"}'
# Book a scheduled call
curl -X POST https://api.vidivo.app/v1/schedule/book \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \
-H "Content-Type: application/json" \
-d '{
"host_username": "janecoach",
"scheduled_at": "2026-03-16T14:00:00Z",
"duration_minutes": 30
}'