API Reference
Comprehensive API documentation for integrating with the Optiphone platform. Base URL: https://app.optiphone.ai
Authentication
Most endpoints require an HTTP-only session cookie. Login via POST /api/login to receive one.
Server-to-server endpoints accept the x-optiphone-api-key header or an apiKey query parameter. Obtain your API key from Settings → API Tokens in the Optiphone dashboard.
Registration, login, password reset, and invitation endpoints are publicly accessible — no auth required.
Authentication
/api/registerRegister a new user account
Request Body
Content-Type: application/json
Schema
{
"email": "string (required, valid email)",
"username": "string (required, min 3 characters)",
"password": "string (required, min 8 characters)",
"firstName": "string (optional)",
"lastName": "string (optional)"
}Example
{
"email": "[email protected]",
"username": "johndoe",
"password": "securepassword123",
"firstName": "John",
"lastName": "Doe"
}Response Example
{
"id": 1,
"email": "[email protected]",
"username": "johndoe",
"firstName": "John",
"lastName": "Doe",
"systemRole": "user"
}/api/loginAuthenticate user and create session
Request Body
Content-Type: application/json
Schema
{
"username": "string (required, case-insensitive)",
"password": "string (required)"
}Example
{
"username": "johndoe",
"password": "securepassword123"
}Response Example
{
"id": 1,
"email": "[email protected]",
"username": "johndoe",
"firstName": "John",
"lastName": "Doe"
}/api/logoutLogout and destroy session
Response Example
{
"success": true
}/api/user-profileGet current authenticated user profile with organization memberships
Response Example
{
"id": 1,
"email": "[email protected]",
"username": "johndoe",
"firstName": "John",
"lastName": "Doe",
"systemRole": "user",
"organizationMemberships": []
}/api/user-profileUpdate current user profile
Request Body
Content-Type: application/json
Schema
{
"firstName": "string (optional)",
"lastName": "string (optional)",
"email": "string (optional, valid email)",
"lastActiveOrganizationId": "number (optional)"
}Example
{
"firstName": "Jane",
"lastName": "Smith",
"email": "[email protected]"
}/api/role-management/meGet current user with full organization details
/api/password-reset/requestRequest password reset email
Request Body
Content-Type: application/json
Schema
{
"email": "string (required, valid email)"
}/api/password-reset/validateValidate password reset token
Request Body
Content-Type: application/json
Schema
{
"token": "string (required)"
}/api/password-reset/confirmConfirm password reset with new password
Request Body
Content-Type: application/json
Schema
{
"token": "string (required)",
"newPassword": "string (required, min 8 characters)"
}Organizations
/api/organizationGet current organization settings
Response Example
{
"id": 1,
"organizationId": 1,
"serviceArea": "4218, 4220",
"defaultJobStatus": "New",
"callForwardingNumber": "+61400000000"
}/api/organizationUpdate current organization settings (requires org_admin)
Request Body
Content-Type: application/json
Schema
{
"serviceArea": "string (optional)",
"callPreviewWaitTime": "number (optional)",
"defaultJobStatus": "string (optional)",
"callForwardingNumber": "string (optional)",
"operationhours": "string (optional)",
"responsetime": "string (optional)"
}/api/organization/subscriptionGet current organization subscription
/api/organization/featuresGet current organization features and access status
/api/organization/usageGet current organization usage statistics
Assistants
/api/assistantsGet all assistants (filtered by user's organizations)
Query Parameters
organizationIdnumber— Filter by organization IDResponse Example
[
{
"assistantId": "assistant-123",
"name": "Customer Service Assistant",
"phoneNumber": "+61400000000",
"organizationId": 1
}
]/api/assistants/:assistantIdGet single assistant by ID
Path Parameters
assistantIdstring— Assistant ID/api/assistantsCreate new assistant
Request Body
Content-Type: application/json
Schema
{
"assistantId": "string (optional, auto-generated if not provided)",
"name": "string (optional)",
"phoneNumber": "string (optional)",
"description": "string (optional)",
"organizationId": "number (optional, uses current org if not provided)",
"isActive": "boolean (optional)",
"greetingPhrase": "string (optional)",
"businessHours": "string (optional, JSON)",
"prompt": "string (optional)"
}/api/assistants/:assistantIdUpdate assistant (requires org_admin for assistant's organization)
Path Parameters
assistantIdstring— Assistant IDRequest Body
Content-Type: application/json
Schema
{
"name": "string (optional)",
"phoneNumber": "string (optional)",
"description": "string (optional)",
"organizationId": "number (optional)",
"isActive": "boolean (optional)",
"greetingPhrase": "string (optional)",
"businessHours": "string (optional)",
"prompt": "string (optional)"
}/api/assistants/:assistantIdDelete assistant (requires org_admin)
Path Parameters
assistantIdstring— Assistant ID/api/assistants/:assistantId/prompt-versionsGet prompt version history for assistant
Path Parameters
assistantIdstring— Assistant ID/api/assistants/:assistantId/prompt-versions/:versionId/activateActivate a specific prompt version
Path Parameters
assistantIdstring— Assistant IDversionIdnumber— Version IDCall Logs
/api/call-logsGet call logs with filtering and pagination
Query Parameters
startDatestring (ISO date)— Start date filterendDatestring (ISO date)— End date filtersearchstring— Search in transcript/summaryassistantIdstring— Filter by assistantorganizationIdnumber— Filter by organizationpostcodestring— Filter by caller postcodecallerNamestring— Filter by caller namephoneNumberstring— Filter by phone numbersuccessRatingnumber— Filter by success rating (1-10)callReasonstring— Filter by call reasonstatusstring— Filter by call statusdurationnumber— Filter by minimum duration (seconds)pagenumber— Page number (default: 1)limitnumber— Items per page (default: 50)Response Example
{
"calls": [],
"total": 0,
"page": 1,
"limit": 50
}/api/call-logs/:callIdGet single call log by ID
Path Parameters
callIdstring— Call log ID/api/call-logs/exportExport call logs as CSV
Query Parameters
startDatestring (ISO date)— Start date filterendDatestring (ISO date)— End date filtersearchstring— Search filterassistantIdstring— Filter by assistantorganizationIdnumber— Filter by organization/api/call-logsAPI KeyCreate a call log from an external provider (ElevenLabs, etc.)
Query Parameters
apiKeystringrequired— Your Optiphone API key (alternatively pass as x-optiphone-api-key header)Request Body
Content-Type: application/json
Schema
{
"id": "string (optional, auto-generated if omitted)",
"externalId": "string (optional, provider-side call ID for idempotency)",
"assistantId": "string (optional)",
"organizationId": "number (optional)",
"isAssigned": "boolean (optional)",
"source": "string (optional, e.g. \"elevenlabs\" | \"vapi\")",
"callReason": "string (optional)",
"status": "string (optional)",
"endedReason": "string (optional)",
"successEvaluation": "number 1–10 (optional)",
"startedAt": "string | number | Date (optional, ISO 8601 or Unix ms)",
"endedAt": "string | number | Date (optional)",
"phoneNumber": "string (optional)",
"customerNumber": "string (optional)",
"callerName": "string (optional)",
"callerEmail": "string (optional)",
"callerAddress": "string (optional)",
"callerPostcode": "string (optional)",
"transcript": "string (optional)",
"summary": "string (optional)",
"recordingUrl": "string (optional)",
"messages": "array (optional)",
"analysis": "object (optional)",
"structuredOutputs": "object (optional)",
"cost": "number (optional)",
"costBreakdown": "object (optional)",
"durationSeconds": "number (optional)"
}Example
{
"externalId": "el_call_abc123",
"source": "elevenlabs",
"assistantId": "asst_xyz",
"organizationId": 1,
"status": "completed",
"endedReason": "customer-ended-call",
"successEvaluation": 8,
"startedAt": "2025-02-24T10:00:00.000Z",
"endedAt": "2025-02-24T10:03:45.000Z",
"durationSeconds": 225,
"phoneNumber": "+61299990000",
"customerNumber": "+61400000001",
"callerName": "Jane Smith",
"callerEmail": "[email protected]",
"callerPostcode": "2000",
"transcript": "Agent: Hello, how can I help you today?...",
"summary": "Customer enquired about service availability.",
"recordingUrl": "https://storage.elevenlabs.io/recordings/el_call_abc123.mp3",
"cost": 0.045,
"costBreakdown": {
"tts": 0.02,
"stt": 0.025
}
}Response Example
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "el_call_abc123",
"assistantId": "asst_xyz",
"organizationId": 1,
"source": "elevenlabs",
"status": "completed",
"endedReason": "customer-ended-call",
"successEvaluation": 8,
"createdAt": 1740391200000,
"updatedAt": 1740391200000,
"startedAt": 1740391200000,
"endedAt": 1740391425000,
"phoneNumber": "+61299990000",
"customerNumber": "+61400000001",
"callerName": "Jane Smith",
"callerEmail": "[email protected]",
"callerPostcode": "2000",
"transcript": "Agent: Hello, how can I help you today?...",
"summary": "Customer enquired about service availability.",
"recordingUrl": "https://storage.elevenlabs.io/recordings/el_call_abc123.mp3",
"durationSeconds": 225
}/api/call-logs/:callIdAPI KeyUpsert a call log by provider-assigned ID — creates if not found, updates if it exists
Path Parameters
callIdstring— Provider call ID used as the record's primary keyQuery Parameters
apiKeystringrequired— Your Optiphone API key (alternatively pass as x-optiphone-api-key header)Request Body
Content-Type: application/json
Schema
{
"externalId": "string (optional, provider-side call ID for idempotency)",
"assistantId": "string (optional)",
"organizationId": "number (optional)",
"source": "string (optional, e.g. \"elevenlabs\" | \"vapi\")",
"callReason": "string (optional)",
"status": "string (optional)",
"endedReason": "string (optional)",
"successEvaluation": "number 1–10 (optional)",
"startedAt": "string | number | Date (optional)",
"endedAt": "string | number | Date (optional)",
"phoneNumber": "string (optional)",
"customerNumber": "string (optional)",
"callerName": "string (optional)",
"callerEmail": "string (optional)",
"callerAddress": "string (optional)",
"callerPostcode": "string (optional)",
"transcript": "string (optional)",
"summary": "string (optional)",
"recordingUrl": "string (optional)",
"messages": "array (optional)",
"analysis": "object (optional)",
"structuredOutputs": "object (optional)",
"cost": "number (optional)",
"costBreakdown": "object (optional)",
"durationSeconds": "number (optional)"
}Example
{
"externalId": "el_call_abc123",
"source": "elevenlabs",
"assistantId": "asst_xyz",
"organizationId": 1,
"status": "completed",
"endedReason": "customer-ended-call",
"successEvaluation": 8,
"startedAt": "2025-02-24T10:00:00.000Z",
"endedAt": "2025-02-24T10:03:45.000Z",
"durationSeconds": 225,
"phoneNumber": "+61299990000",
"customerNumber": "+61400000001",
"callerName": "Jane Smith",
"callerEmail": "[email protected]",
"callerPostcode": "2000",
"transcript": "Agent: Hello, how can I help you today?...",
"summary": "Customer enquired about service availability.",
"recordingUrl": "https://storage.elevenlabs.io/recordings/el_call_abc123.mp3",
"cost": 0.045,
"costBreakdown": {
"tts": 0.02,
"stt": 0.025
}
}Contacts
/api/contactsGet all contacts for organization
Query Parameters
organizationIdnumberrequired— Organization IDsearchstring— Search in name, email, phonelimitnumber— Limit resultsoffsetnumber— Offset for pagination/api/contacts/:idGet single contact by ID
Path Parameters
idnumber— Contact IDQuery Parameters
organizationIdnumberrequired— Organization ID/api/contactsCreate new contact
Request Body
Content-Type: application/json
Schema
{
"organizationId": "number (required)",
"firstName": "string (optional)",
"lastName": "string (optional)",
"email": "string (optional)",
"phone": "string (optional)",
"businessName": "string (optional)",
"address": "string (optional)",
"postcode": "string (optional)",
"tags": "array<string> (optional)"
}/api/contacts/:idUpdate contact
Path Parameters
idnumber— Contact IDRequest Body
Content-Type: application/json
Schema
{
"organizationId": "number (required)",
"firstName": "string (optional)",
"lastName": "string (optional)",
"email": "string (optional)",
"phone": "string (optional)",
"businessName": "string (optional)",
"address": "string (optional)",
"postcode": "string (optional)",
"tags": "array<string> (optional)",
"lastInteractionAt": "number (optional, timestamp)",
"lastInteractionType": "string (optional)",
"lastInquiryStatus": "string (optional)"
}/api/contacts/:idDelete contact (soft delete)
Path Parameters
idnumber— Contact IDQuery Parameters
organizationIdnumberrequired— Organization IDMetrics
/api/metrics/:assistantIdGet assistant metrics (total calls, avg duration, success rate)
Path Parameters
assistantIdstring— Assistant IDQuery Parameters
timeRangestring— Time range (e.g., 'January 2024' or ISO date range)Response Example
{
"totalCalls": 150,
"avgDuration": 120,
"successRate": 85.5
}/api/call-volume/:assistantIdGet call volume data by date
Path Parameters
assistantIdstring— Assistant IDQuery Parameters
timeRangestring— Time rangeResponse Example
[
{
"date": "2024-01-01",
"count": 5
},
{
"date": "2024-01-02",
"count": 8
}
]Billing
/api/billing/balanceGet current organization credit balance and plan info
Response Example
{
"balance": {
"balance": 100,
"totalEarned": 500,
"totalSpent": 400
},
"subscription": {
"planId": "solo",
"status": "active"
},
"planPricing": {
"monthlyPrice": null,
"includedMinutes": null
},
"monthlyUsage": 50
}/api/billing/plansGet available subscription plans with pricing
Response Example
{
"plans": [
{
"planId": "solo",
"name": "Solo",
"monthlyPrice": null,
"includedMinutes": null
}
]
}/api/billing/historyGet credit transaction history
Query Parameters
typestring— Transaction type (subscription, refill, usage, adjustment)startDatenumber (timestamp)— Start dateendDatenumber (timestamp)— End datelimitnumber— Limit results (default: 100)offsetnumber— Offset (default: 0)/api/billing/checkout/planCreate Stripe checkout session for plan upgrade
Request Body
Content-Type: application/json
Schema
{
"planId": "string (required, one of: tradesman, crew, enterprise)"
}Response Example
{
"checkoutUrl": "https://checkout.stripe.com/..."
}/api/billing/checkout/refillCreate Stripe checkout session for credit refill
Request Body
Content-Type: application/json
Schema
{
"minutes": "number (required)",
"planId": "string (required)"
}/api/billing/portalCreate Stripe billing portal session
Response Example
{
"portalUrl": "https://billing.stripe.com/..."
}/api/billing/payment-methodUpdate payment method
Request Body
Content-Type: application/json
Schema
{
"paymentMethodId": "string (required, Stripe payment method ID)"
}/api/billing/payment-methodGet current payment method
Webhooks
/api/subscriptions/webhookAPI KeyWebhook endpoint for Stripe and internal Optiphone webhooks
Query Parameters
apiKeystringrequired— Your Optiphone API key (alternatively pass as x-optiphone-api-key header)Notifications
/api/notification-preferencesGet user notification preferences
/api/notification-preferencesUpdate user notification preferences
Request Body
Content-Type: application/json
Schema
{
"pushNotificationsEnabled": "boolean (optional)",
"quietHoursEnabled": "boolean (optional)",
"quietHoursStart": "string (optional, HH:mm format)",
"quietHoursEnd": "string (optional, HH:mm format)",
"notifyOnNewCall": "boolean (optional)"
}/api/push-notifications/subscribeSubscribe to push notifications
Request Body
Content-Type: application/json
Schema
{
"subscription": {
"endpoint": "string (required, URL)",
"keys": {
"p256dh": "string (required)",
"auth": "string (required)"
}
}
}/api/push-notifications/unsubscribeUnsubscribe from push notifications
/api/push-notifications/vapid-keyGet VAPID public key for push notifications
Preferences
/api/user-preferencesGet user preferences
/api/user-preferencesUpdate user preferences
Request Body
Content-Type: application/json
Schema
{
"preferences": "object (required, JSON object)"
}Invitations
/api/invitations/validateValidate organization invitation token
Query Parameters
tokenstringrequired— Invitation token/api/invitations/acceptAccept organization invitation
Request Body
Content-Type: application/json
Schema
{
"token": "string (required)",
"password": "string (required, min 8 characters, if creating new account)",
"email": "string (optional, if creating new account)",
"username": "string (optional, if creating new account)"
}Agent Tools (Voice Agent Webhooks)
/api/agent-tools/check-availabilityAPI KeyVoice-agent webhook: find open appointment slots on the org's calendar during a live call.
Request Body
Content-Type: application/json
Schema
{
"organizationId": "number (required) — the calling org (supplied by the agent, not the caller)",
"call_sid": "string (required) — Twilio call SID; cross-checked against the active call",
"with_person": "string (optional) — a named team member to book with; omit for next available",
"days": "number (optional) — lookahead window in days (default 7)"
}Example
{
"organizationId": 1,
"call_sid": "CA…",
"with_person": "Dylan",
"days": 7
}Response Example
{
"hasAvailability": true,
"slots": [
"Wednesday 1 Jul, 9:00 AM",
"Wednesday 1 Jul, 10:00 AM"
]
}/api/agent-tools/book-appointmentAPI KeyVoice-agent webhook: book a chosen slot into the org's calendar during a live call.
Request Body
Content-Type: application/json
Schema
{
"organizationId": "number (required)",
"start_at": "number (required) — chosen slot start, epoch ms, from check-availability",
"call_sid": "string (required)",
"contact_phone": "string (required) — caller's number for the booking",
"contact_name": "string (optional)",
"reason": "string (optional) — what the appointment is for",
"with_person": "string (optional) — named team member to book with"
}Example
{
"organizationId": 1,
"start_at": 1782730800000,
"call_sid": "CA…",
"contact_phone": "+61…",
"contact_name": "Jane Smith",
"reason": "Quote for switchboard upgrade"
}Response Example
{
"confirmed": true,
"jobId": 482,
"startAt": 1782730800000,
"endAt": 1782734400000
}/api/agent-tools/find-appointmentsAPI KeyVoice-agent webhook: list a caller's upcoming appointments (earliest first) so the agent can answer "when is my appointment?" and pick one to change — without inventing details.
Request Body
Content-Type: application/json
Schema
{
"organizationId": "number (required)",
"call_sid": "string (required)",
"phone": "string (optional) — number the appointments are under; defaults to the caller's number"
}Example
{
"organizationId": 1,
"call_sid": "CA…",
"phone": "+61…"
}Response Example
{
"appointments": [
{
"appointmentId": 482,
"when": "Wednesday 1 Jul, 9:00 AM",
"startAt": 1782730800000,
"calendarName": "Dylan Amey",
"withPerson": "Dylan",
"reason": "Quote"
}
]
}/api/agent-tools/reschedule-appointmentAPI KeyVoice-agent webhook: move a specific existing appointment to a new slot during a live call (no duplicate left behind).
Request Body
Content-Type: application/json
Schema
{
"organizationId": "number (required)",
"appointmentId": "number (required) — from find-appointments",
"newStartAt": "number (required) — new slot start, epoch ms, from check-availability",
"call_sid": "string (required)"
}Example
{
"organizationId": 1,
"appointmentId": 482,
"newStartAt": 1782817200000,
"call_sid": "CA…"
}Response Example
{
"rescheduled": true,
"jobId": 482,
"startAt": 1782817200000,
"endAt": 1782820800000,
"when": "Thursday 2 Jul, 9:00 AM",
"calendarName": "Dylan Amey"
}/api/agent-tools/cancel-appointmentAPI KeyVoice-agent webhook: cancel a specific existing appointment during a live call.
Request Body
Content-Type: application/json
Schema
{
"organizationId": "number (required)",
"appointmentId": "number (required) — from find-appointments",
"call_sid": "string (required)"
}Example
{
"organizationId": 1,
"appointmentId": 482,
"call_sid": "CA…"
}Response Example
{
"cancelled": true,
"jobId": 482
}