Noveum docs
ProductsNovaSynthUse the APIStart voice tests with the Calls API

Developer Calls API

Start supported NovaSynth Phone or LiveKit tests from scripts and CI, poll their status, and export or replay a redacted call configuration.

The Developer Calls API runs a NovaSynth test without pre-registering a project, persona, scenario, or connection in the dashboard. Send the endpoint credentials and test definition with each request, then poll the returned status URL until the call finishes.

Runtime execution currently supports Phone and LiveKit endpoints in Voice mode only. This applies both to credentials supplied inline and to a saved connection referenced by ID. A request with "mode": "text" returns 400, and a webhook value returns 501 because webhook delivery is not supported; poll statusUrl instead. Use a dashboard batch in Text mode for HTTP Chat.

Calls land in a Developer API project by default. Pass an optional projectId to run them in an existing project of the API key's organization.

Endpoint support

Endpoint typeInline requestExisting connectionCurrent result
PhoneSupportedSupportedTest is queued
LiveKitSupportedSupportedTest is queued
HTTP ChatNot acceptedNot supportedAn existing connection returns 501 Not Implemented
WebSocket, Pipecat, VAPI, Retell, ElevenLabs Conversational AIRequest shape recognized, but not supportedNot supportedReturns 501 Not Implemented before dispatch

The request validator recognizes the additional inline credential shapes, but a separate runtime gate rejects them before dispatch. Accepting their request shape is not execution support. See the connection support matrix for dashboard batches, persistent endpoint records, direction, and provider webhooks.

Authentication and base URL

Create an organization-scoped API key in the Noveum dashboard and send it as a bearer token:

Code
export NOVEUM_API_KEY="replace-with-your-key"
export NOVEUM_API_BASE="https://api.noveum.ai/api"

Do not put provider secrets or the Noveum API key in source control. Noveum stores a redacted configuration snapshot for each test so you can export its setup without exposing credentials.

Start one phone test

Code
curl --fail-with-body --silent --show-error \
  --request POST "$NOVEUM_API_BASE/v1/developer/calls" \
  --header "Authorization: Bearer $NOVEUM_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "endpoint": {
      "type": "phone",
      "phoneNumber": "+14155550123",
      "phoneCountryCode": "+1"
    },
    "persona": {
      "name": "Frustrated subscriber",
      "goal": "Cancel a renewal without accepting an upsell",
      "tonePreference": "frustrated"
    },
    "scenario": {
      "name": "Cancellation policy",
      "description": "Ask to cancel, challenge one retention offer, then confirm the cancellation."
    },
    "mode": "voice",
    "maxCallDurationS": 180,
    "serviceVersion": "checkout-agent-2026-07-30"
  }'

Phone numbers must be valid E.164 values and must begin with the supplied dial prefix. A successful request returns 202 Accepted with a callId, batchId, statusUrl, and estimated credit and USD cost.

The inline persona accepts the same fields as a saved persona, including primaryLanguage and ttsVoiceModel. Leave ttsVoiceModel unset to let NovaSynth choose a TTS model that supports the persona's spoken languages. The inline scenario accepts planned interruptions as described in Scenarios. Set "eval": false to run the call without evaluation.

Start one LiveKit test

Use the same endpoint with an inline LiveKit connection:

Code
{
  "endpoint": {
    "type": "livekit",
    "livekitUrl": "wss://your-project.livekit.cloud",
    "livekitApiKey": "your-livekit-key",
    "livekitApiSecret": "your-livekit-secret",
    "livekitAgentName": "support-agent"
  },
  "persona": {
    "name": "New customer",
    "goal": "Ask whether the plan supports two team members"
  },
  "scenario": {
    "name": "Plan question",
    "description": "Ask about the entry plan and verify the seat limit."
  },
  "mode": "voice",
  "maxCallDurationS": 180
}

The API accepts an existing NovaSynth connection through { "type": "existing", "endpointId": "..." } as well, but the connection must belong to the API key's organization and its type must currently be Phone or LiveKit. Other saved connection types return 501 Not Implemented.

Poll the result

Code
curl --fail-with-body --silent --show-error \
  --header "Authorization: Bearer $NOVEUM_API_KEY" \
  "$NOVEUM_API_BASE/v1/developer/calls/$CALL_ID"

Poll about every three seconds until status is completed, failed, or cancelled. The result can include:

  • duration and normalized transcript turns;
  • a temporary recording URL and its expiration time;
  • evaluation status, score, pass/fail result, summary, and scorer results;
  • an error and completion timestamp.

Use the returned recordingUrlExpiresAt value rather than persisting the signed recording URL.

Batch tests

POST /v1/developer/calls/batch accepts a shared endpoint and up to 100 persona/scenario pairs in calls. Set concurrencyLimit from 1 to 20; it defaults to 5. The response includes the batch ID, call IDs, total call count, status URL, and estimated total cost.

Poll GET /v1/developer/calls/batch/:id for completed and failed counts, pass rate, average score, and the status of each call.

Inbound calls

Set "direction": "inbound" to test an outbound bot: Noveum reserves one of its numbers for the synthetic persona, and your bot dials it. Inbound calls need a Phone endpoint. A saved Phone connection must also be configured for inbound calls.

Create the call. The response adds runId, claimUrl, and an inbound statusUrl under /v1/novasynth/inbound/runs/{runId}.
Poll that statusUrl until status is ready. The response then includes phoneNumber, dialWindowClosesAt, profile, personaName, and scenarioName.
Have your bot dial phoneNumber before dialWindowClosesAt, passing profile to it as it would a real customer's details.
Read the results from resultsUrl after the call completes.

When no number is free, the status is waiting with reason set to no_number_available and a retryAfterMs hint; retry claimUrl for the same run. If arming fails after the call was created, the response says so and includes resultsUrl and claimUrl. Inspect the call instead of creating it again.

An inbound batch runs one call at a time and arms only its first call. The batch response includes an inbound object for that call and a claimNextUrl. Finish the first call, then POST to claimNextUrl to arm the next one, and poll each returned run until it is ready. See Inbound calls for a ready-made dialling loop.

Export and replay configurations

Calls created through this API retain a redacted configuration snapshot:

EndpointPurpose
GET /v1/developer/calls/:id/configExport one call as JSON
GET /v1/developer/calls/:id/config?format=curlExport a cURL example
GET /v1/developer/calls/:id/config?format=pythonExport a polling Python script that also handles inbound claiming
GET /v1/developer/calls/batch/:id/configExport a whole batch configuration
POST /v1/developer/calls/:id/replayReplay when the stored snapshot still contains usable credentials

Secret fields are replaced with placeholders in stored snapshots. In the normal case, export the configuration, restore secrets from your secret manager, and submit it as a new call. A direct replay returns an error when the redacted snapshot cannot pass validation.

Operational checks

  • Estimate the call count and duration before a CI run; quota is checked before the tests start.
  • Use serviceVersion to associate results with a deployment or commit.
  • Set a client timeout for the create request, then use polling rather than holding the connection open.
  • Record callId and batchId in CI output, but never print endpoint credentials.