Customer applications
Orchestrate governed outbound calls from your application while Skysay runs the managed voice agent.
Use a customer application when your system owns the changing business record and wants Skysay to run the phone conversation. Your application selects the case and recipient, calls the API, answers approved live-data requests through Integrations, and reconciles signed results. Skysay owns the governed call execution.
Ownership boundary
| Your application owns | Skysay owns |
|---|---|
| Recipients, cases, current balances, call cadence, retries, and final business outcomes | Policy- and wallet-gated call execution, managed agent runtime, tool allowlist, recordings/transcripts, and call results |
| CRM, calendar, billing, database, correct-person policy, and business idempotency | Phone number/media operation and Skysay authorization boundaries |
| Live data through an Integration Action | Stable private reference material through a Knowledge Base |
The hosted agent is managed in Skysay. Its AI model can be Gemini or another supported model. The model decides whether to use an allowed read Action; it does not receive your application's credentials or authority to choose arbitrary URLs.
sequenceDiagram
participant App as Your application
participant OP as Skysay
participant Agent as Hosted agent
participant Adapter as Your Integration adapter
participant System as CRM or billing system
App->>OP: POST /v1/calls with durable case reference
OP->>Agent: policy-gated managed call
Agent->>OP: request attached read Action
OP->>Adapter: signed typed JSON request
Adapter->>System: authorize and read current fact
System-->>Adapter: safe projection
Adapter-->>OP: schema-valid JSON result
OP-->>Agent: bounded result
OP-->>App: signed lifecycle and artifact eventsStart one call
Use POST /v1/calls. Supply Idempotency-Key for retries; it is preferred over
the optional idempotency_key body field. The generated API
reference contains the exact current schema.
curl -X POST https://api.skysay.ai/v1/calls \
-H "Authorization: Bearer $SKYSAY_TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: reminder-case-456-attempt-1" \
-d '{
"project_id": "proj_123",
"from_phone_number_id": "num_123",
"to_phone_number": "+1 555 0100",
"country": "US",
"purpose": "payment_reminder",
"agent_id": "agent_123",
"user_authorization": {
"authorized_by": "ops-user-123",
"reason": "customer-approved payment reminder"
},
"external_reference": "case_456",
"external_group_reference": "reminder_batch_2026_08",
"call_context": { "locale": "en-US" }
}'country is optional. Skysay derives the destination country from
to_phone_number's calling code; a country you send is used only when the
number cannot be attributed to a country, and never overrides it.
external_reference is your stable case ID; Skysay stores it but does not
interpret it. external_group_reference can identify your batch. Keep
call_context small and non-secret—at most 32 JSON properties—and use it only
for facts valid when the call begins. It is not a place for case history,
credentials, or authorization tokens. A conversation workflow's read tool is the
one thing served from it: send the fields that tool projects under
call_context.case_snapshot. See Agent workflows.
For a hosted agent with published language profiles, select one with
runtime_config.language in the call request. A locale or
preferred_language property inside call_context is only metadata and does
not select a profile. Your application still owns row overrides and batch
language choices. See Multilingual outbound
calls for configuration and admission behavior.
An agent that could not take the call is refused before anything is spent
If the selected agent's published voice setup is one the voice agent could not
build a call from—no instructions, no exact voice, no greeting where one is
required, or a component that cannot run the call's language—POST /v1/calls
answers 400:
{
"error": "realtime_config_not_runnable",
"reason": "system_prompt_missing",
"blockers": [{ "code": "system_prompt_missing", "scope": "agent", "detail": "" }]
}No call is created, no wallet balance is reserved, and no origination job is queued, so retrying after fixing the agent is an ordinary new request. The codes are the same ones the workspace shows for that agent, so the remedy is the one already named there. Previously such a request was accepted and the call ended before it had any audio.
For a multilingual agent the check covers every published language, not only the
one this call speaks: with Follow the caller, the
voice agent prepares all of them before the call connects, so each blocker's
detail carries the language it belongs to.
A campaign whose agent is in this state finishes each contact as
agent_not_runnable rather than dialling it.
Retry, backpressure, and cancel boundaries
- Repeating the same canonical request with the same idempotency key returns the
original call. Reusing a key for a different request returns
409. - A full bounded outbound queue returns
429withRetry-Afterandretry_after_seconds. Respect that delay and keep your own queue bounded. POST /v1/calls/{call_id}/cancelcan cancel a queued call whose originate job has not been claimed, and it releases the corresponding wallet reservation. It can also cancel a call held for review (needs_review), which never reserved anything. Once a worker has claimed the job, the API returns409rather than pretending a carrier call was stopped.
Read current progress with GET /v1/calls, and fetch recording, transcript,
waveform, and artifact evidence through the call endpoints in the generated
reference. Use signed events for prompt,
authoritative reconciliation rather than assuming an HTTP create response means
the call completed.
Live facts come from typed Actions
Attach only the read Actions that this agent needs. At call time the model can
supply only that Action's declared business arguments. Skysay injects the
canonical organization, project, call, agent, external references, and call
context; it signs the request to your adapter. Your adapter verifies the
signature, checks its own business authorization, dispatches tool.name, and
returns a bounded response matching the Action's output schema.
This is deliberately different from a Knowledge Base:
| Use | Put it here |
|---|---|
| Opening hours, product terms, policy wording, and durable FAQ text | Knowledge Base |
| Current account balance, booking availability, order status, or CRM record | Integration Action |
| A conversation decision or generated wording | The managed model, within the agent's instructions and allowed tools |
Payment reminder example
A payment reminder is a useful boundary test because the business facts change and the conversation is sensitive.
- Your application selects a permitted case and recipient under its own
cadence, suppression, dispute, time-window, and retry rules. It creates a
bounded call with an
external_referencesuch ascase_456. - Skysay independently authorizes the requested call under its policy, wallet, and organization controls, then runs the configured managed agent.
- Stable explanation of your payment policy can live in a Knowledge Base.
Account status and payment link must be requested through a narrow read
Action, never copied into the prompt. The figures a conversation workflow is
allowed to disclose travel as that call's own
call_context.case_snapshot, bounded by the tool's declared projection. - Before your application has established the correct person, its adapter returns a safe projection with no balance, debt, or account history. The hosted agent must not disclose those facts merely because the caller supplied an identifier.
- Your application receives signed terminal and artifact-ready events, reads the evidence it needs, and determines its own next business step. Advisory extraction can assist triage but cannot be the accounting or policy record.
Skysay does not replace your CRM, debt/case system, consent process, or correct-person verification. It provides the governed telephony and managed agent layer around the call.
Build for reconciliation
Persist your own case ID, the returned Skysay call ID, idempotency key, and
the final event ID. Deduplicate signed events by their event id; deliveries
are retryable. Treat terminal lifecycle events and call evidence as authoritative
for the Skysay outcome. Treat your own case state as authoritative for your
business outcome.
For a compact, schema-validated summary after a completed call, configure post-call extraction. It is optional and advisory; it never changes the call, policy, wallet, or customer systems.