Skip to main content
Zelto ingests text conversations — a chatbot, messaging agent, or any text-based assistant (web chat, SMS, WhatsApp, in-app) — over the same endpoint as voice calls: POST https://ingest.zelto.ai/webhooks/calls. A text conversation is just a call with a transcript and no audio. Once it lands, it runs through the full pipeline — transcription is skipped (there’s already a transcript), and everything downstream (summaries, findings, reviews, scorecards) works exactly as it does for voice. The mechanics — minting a key, idempotency, error handling — are shared with Custom & other providers. This page covers what’s specific to text.

Mint a key

Text upload uses the same API key as every other custom upload. Follow Custom & other providers → Mint a key to create one under Settings → Integrations → API Call Upload, then expose it as ZELTO_API_KEY.

Upload a chat conversation

Send the messages as transcript.turnsrole: "user" for the person, role: "assistant" for your agent — and leave the audio and telephony fields out. Only call.externalId plus an agent reference are required.
chat.json
A successful upload returns HTTP 200 with { "received": true }. Send:
  • agent.externalId (Zelto finds or creates the agent) or agentId (an existing agent’s UUID). Use a stable id per agent, not per conversation.
  • call.externalId — a stable, unique id for the conversation (the dedup key).
  • transcript.turns[] — the messages, in order, as user / assistant turns.
  • call.startedAt / call.endedAt — the conversation’s start and end timestamps (ISO 8601). Optional, but they let you sort and window by time.
  • metadata.channel — record the channel (web-chat, sms, whatsapp, in-app) so you can filter on it later. There’s no dedicated channel field.
Omit:
  • recordingUrl and recordingUploadId — there’s no audio to re-host.
  • Per-turn startSeconds / endSeconds and words[] — those are for word-synchronized audio playback.
  • durationSeconds and customer.number — voice/telephony fields with no text equivalent.
A delivery with neither a transcript nor a recording is treated as an empty dial: Zelto acks it with 200 but creates no conversation, since there’s nothing to analyze. A text conversation must carry at least one transcript.turns entry.

Tool calls

If your chatbot invokes tools mid-conversation (check availability, look up an order), forward each as a tool turn carrying a structured toolCall — the same shape voice agents use. Zelto shows it inline in the transcript and feeds it into analysis. See Tool calls for the field reference and an example.

Idempotent re-uploads

call.externalId is the dedup key. Re-POSTing the same id updates the conversation in place and never double-charges, so retries and later enrichment re-sends are both safe. See Idempotent re-uploads.

Conversations display as “calls”

Text conversations ingest and analyze fully, but Zelto’s model and UI are voice-first: an ingested conversation is labeled a “call” throughout, and its name defaults to Call <externalId> unless you give the agent a friendlier name. There’s no first-class channel or modality field yet — the channel you put in metadata is stored and queryable, but the interface won’t badge the conversation as chat. Everything analytical still works; only the labeling is voice-flavored.
  • Custom & other providers — the shared upload contract: keys, errors, and native Vapi/Retell payloads.
  • Conversations — where uploaded conversations land.
  • API reference — REST conventions and the full request shape, with a playground to test a payload.
  • MCP — the same API key gives connected editors read/write access.