Call Links
Call links are shareable URLs that guests use to initiate a video call session with a host. Each link encodes the host’s rate, window size, and access configuration.
A call link URL looks like:
https://vidivo.app/c/{short_code}POST /links
Section titled “POST /links”Create a new call link.
Authentication: Bearer token with host role.
Request
Section titled “Request”POST /v1/linksAuthorization: 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", "scheduled_start": null, "scheduled_end": null}| Field | Type | Required | Description |
|---|---|---|---|
rate_per_minute | string (decimal) | Yes | Per-minute charge in USD. Min $0.50, max $999.99. |
window_size_minutes | integer | Yes | Billing window size in minutes. Min 1, max 60. |
expires_at | string (ISO 8601) | No | When the link expires. Null = never expires. |
max_uses | integer | No | Maximum number of times the link can be used. Null = unlimited. |
label | string | No | Internal label for your reference. Max 120 characters. |
scheduled_start | string (ISO 8601) | No | Link only works after this time. |
scheduled_end | string (ISO 8601) | No | Link stops working after this time. |
Response
Section titled “Response”HTTP/1.1 201 CreatedContent-Type: application/json{ "link": { "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", "scheduled_start": null, "scheduled_end": null, "is_active": true, "created_at": "2026-03-15T10:00:00Z" }}Errors
Section titled “Errors”| Code | Status | Description |
|---|---|---|
forbidden | 403 | User is not a verified host |
validation_error | 400 | Invalid rate, window size, or date |
stripe_not_connected | 402 | Host has not completed Stripe onboarding |
GET /links
Section titled “GET /links”List all call links for the authenticated host.
Authentication: Bearer token with host role.
Request
Section titled “Request”GET /v1/links?limit=20&cursor=eyJpZCI6IjEyMyJ9&active=trueAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...| Query Parameter | Type | Description |
|---|---|---|
limit | integer | Results per page. Default 20, max 100. |
cursor | string | Pagination cursor from previous response. |
active | boolean | Filter by active status. Omit for all. |
Response
Section titled “Response”HTTP/1.1 200 OKContent-Type: application/json{ "links": [ { "id": "01JN2X7N1Q6I0TSCUYZ9AELGHI", "short_code": "abc123", "url": "https://vidivo.app/c/abc123", "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" } ], "pagination": { "cursor": null, "has_more": false, "limit": 20 }}GET /links/{short_code}
Section titled “GET /links/{short_code}”Retrieve a single call link by its short code. This endpoint is publicly accessible — it is used by guests when they open a link.
Authentication: None required for public metadata. Bearer token required for full details (use count, label, etc.).
Request
Section titled “Request”GET /v1/links/abc123Response (public)
Section titled “Response (public)”HTTP/1.1 200 OKContent-Type: application/json{ "link": { "short_code": "abc123", "url": "https://vidivo.app/c/abc123", "host": { "display_name": "Jane Smith" }, "rate_per_minute": "2.50", "window_size_minutes": 10, "is_active": true, "scheduled_start": null, "scheduled_end": null }}Errors
Section titled “Errors”| Code | Status | Description |
|---|---|---|
not_found | 404 | Link does not exist or has been deleted |
link_expired | 410 | Link existed but has expired |
link_exhausted | 410 | Link has reached its maximum use count |
DELETE /links/{id}
Section titled “DELETE /links/{id}”Deactivate a call link. The link is soft-deleted — it cannot be used for new calls but existing sessions continue unaffected.
Authentication: Bearer token with host role. Must be the link owner.
Request
Section titled “Request”DELETE /v1/links/01JN2X7N1Q6I0TSCUYZ9AELGHIAuthorization: Bearer eyJhbGciOiJSUzI1NiJ9...Response
Section titled “Response”HTTP/1.1 204 No ContentErrors
Section titled “Errors”| Code | Status | Description |
|---|---|---|
not_found | 404 | Link not found |
forbidden | 403 | Caller does not own this link |
Link Configuration Options
Section titled “Link Configuration Options”Rate and Billing
Section titled “Rate and Billing”| Setting | Description | Constraints |
|---|---|---|
rate_per_minute | Cost per minute in USD | $0.50 – $999.99 |
window_size_minutes | Pre-authorization window | 1 – 60 minutes |
Choosing a window size: Smaller windows (e.g. 5 min) mean more frequent holds and captures but allow guests to stop at finer granularity. Larger windows (e.g. 30 min) reduce Stripe API calls but hold more funds upfront.
Access Control
Section titled “Access Control”| Setting | Description | Example |
|---|---|---|
expires_at | Hard expiry date for the link | 24 hours, 7 days, never |
max_uses | Maximum number of call sessions | 1 (single-use), null (unlimited) |
scheduled_start | Link not usable before this time | Session start time |
scheduled_end | Link not usable after this time | Session end time |
Recommended Patterns
Section titled “Recommended Patterns”Single-use session (most secure):
{ "max_uses": 1, "expires_at": "2026-03-16T10:00:00Z"}Permanent open link (recurring clients):
{ "max_uses": null, "expires_at": null}Scheduled appointment:
{ "max_uses": 1, "scheduled_start": "2026-03-15T14:00:00Z", "scheduled_end": "2026-03-15T15:00:00Z"}Code Examples
Section titled “Code Examples”# Create a single-use linkcurl -X POST https://api.vidivo.app/v1/links \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..." \ -H "Content-Type: application/json" \ -d '{ "rate_per_minute": "2.50", "window_size_minutes": 10, "max_uses": 1, "expires_at": "2026-03-22T00:00:00Z", "label": "Session with Alex" }'
# List active linkscurl "https://api.vidivo.app/v1/links?active=true" \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..."
# Get a link (public)curl https://api.vidivo.app/v1/links/abc123
# Delete a linkcurl -X DELETE https://api.vidivo.app/v1/links/01JN2X7N1Q6I0TSCUYZ9AELGHI \ -H "Authorization: Bearer eyJhbGciOiJSUzI1NiJ9..."const API = 'https://api.vidivo.app/v1';
// Create a call linkconst { link } = await fetch(`${API}/links`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, }, body: JSON.stringify({ rate_per_minute: '2.50', window_size_minutes: 10, max_uses: 1, expires_at: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), label: 'Session with Alex', }),}).then(r => r.json());
console.log(`Share this link: ${link.url}`);
// List all linksconst { links } = await fetch(`${API}/links?active=true`, { headers: { Authorization: `Bearer ${accessToken}` },}).then(r => r.json());type CreateLinkRequest struct { RatePerMinute string `json:"rate_per_minute"` WindowSizeMinutes int `json:"window_size_minutes"` ExpiresAt *string `json:"expires_at,omitempty"` MaxUses *int `json:"max_uses,omitempty"` Label string `json:"label,omitempty"`}
func createLink(accessToken string) error { maxUses := 1 expiresAt := "2026-03-22T00:00:00Z"
body, _ := json.Marshal(CreateLinkRequest{ RatePerMinute: "2.50", WindowSizeMinutes: 10, ExpiresAt: &expiresAt, MaxUses: &maxUses, Label: "Session with Alex", })
req, _ := http.NewRequest("POST", "https://api.vidivo.app/v1/links", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+accessToken)
resp, err := http.DefaultClient.Do(req) if err != nil { return err } defer resp.Body.Close()
var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) link := result["link"].(map[string]interface{}) fmt.Printf("Created link: %s\n", link["url"]) return nil}