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
}
}/api/outbound-callsAPI KeyPlace an outbound call to one contact, or a batch of up to 25, using one of the organization's own assistants
Request Body
Content-Type: application/json
Schema
{
"organizationId": "number (required, body field not query param):must be the organization the token belongs to",
"assistantId": "string (required, body field not query param):an assistant in that organization (from GET /api/assistants); any assistant can dial out, there is no separate \"outbound assistant\"",
"contact": "object (required unless contacts):a single contact reference; mutually exclusive with contacts",
"contacts": "array (required unless contact):up to 25 contact references; mutually exclusive with contact",
"contact / contacts[].contactId": "number (optional):an existing contact in this organization; wins when both contactId and phone are given",
"contact / contacts[].phone": "string (optional):normalised to E.164, then matched to an existing contact in this organization",
"contact / contacts[].sourceCallLogId": "string (optional):call log ID of an earlier call this one follows up on; set per contact, not per request"
}Example
{
"organizationId": 1,
"assistantId": "78705525-4257-4018-a9dc-90a9ebca0735",
"contacts": [
{
"contactId": 7
},
{
"phone": "0412 345 678"
},
{
"contactId": 9,
"sourceCallLogId": "550e8400-e29b-41d4-a716-446655440000"
}
]
}Response Example
{
"organizationId": 1,
"assistantId": "78705525-4257-4018-a9dc-90a9ebca0735",
"requested": 3,
"placed": 1,
"failed": 1,
"skipped": 1,
"idempotencyWindowMinutes": 10,
"results": [
{
"index": 0,
"contactId": 7,
"phone": null,
"status": "placed",
"callSid": "CA2f8c1d94e7b34a05",
"callLogId": "9f1c7b20-4d3e-4a1f-8c62-0b7d5e2a1c34"
},
{
"index": 1,
"contactId": null,
"phone": "+61412345678",
"status": "failed",
"reason": "contact-not-found"
},
{
"index": 2,
"contactId": 9,
"phone": null,
"status": "skipped",
"reason": "recently-called",
"callLogId": "6b1f2c34-8ad9-4f0e-9c21-1b2c3d4e5f60"
}
]
}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
}