1
Stage the audio
Call
POST /api/v1/voice-notes with a genuine CAF file or an uncompressed
WAV file. Chert returns a temporary voice_note_id and the
phone_line_id that staged it.2
Send from the same line
Call
POST /api/v1/send with the returned voice_note_id and the exact
same phone_line_id before the staged audio expires.This two-step endpoint flow is for outbound native voice notes. Inbound
Apple voice memos arrive through the normal
message.received attachment
flow—there is no separate inbound voice-note endpoint. See
Receiving inbound voice memos.Prerequisites
You need:- A Chert API signing secret.
- A phone line assigned to the authenticated project.
- Voice-note support enabled on the selected phone line.
- A recipient phone number or Apple ID email address.
- A genuine CAF file or an uncompressed WAV file.
- One
phone_line_idused for both staging and sending.
GET /api/v1/phone-numbers:
is_active is true and capabilities includes send.
The list does not currently expose a separate voice-note capability flag, so
use a line Chert has enabled for voice notes. See Phone numbers
for the complete response reference.
Authentication
The examples use bearer authentication for readability:x-chert-tenant with the
tenant slug. HMAC signing is recommended when you do not want to transmit the
secret as a bearer token. See Authentication for both
modes and the five-minute signature window.
Never place a signing secret in client-side code, source control, logs, or an
example payload.
Stage a voice note
audio_url or audio_base64.
HTTPS audio URL
audio_url must use HTTPS and must remain reachable while the staging request
runs.
Inline base64 WAV
jq so the base64 payload is not
printed or interpolated into shell history:
Request fields
Supported formats
The production allowlist is:
Renaming or relabeling M4A, AAC, MP3, WebM, Ogg, compressed WAV, or another
format as
audio/caf does not turn it into a valid CAF file. Convert an
unsupported source to uncompressed WAV before staging it:
Duration and staging lifetime
Chert probes the actual audio duration. A caller-provided
duration_seconds
value is informational and does not override the measured duration.
Staged IDs are temporary and line-local. A phone-line service restart
invalidates them even if expires_at_ms has not passed. Stage only when your
application is ready to send. If an ID expires or the selected line restarts,
stage the audio again and use the new voice_note_id.
Staging response
Send the staged voice note
- Copy the exact
phone_line_idfrom the staging response. - Send before
expires_at_ms. phoneaccepts an E.164 number or an Apple ID email handle; Chert lowercases email handles during normalization.- Use an
idempotency_keyfor every intended send.
Send response
Complete cURL walkthrough: HTTPS URL
This walkthrough selects an assigned active send line, stages the audio, reads both returned IDs, and sends with one stable idempotency key.curl --fail-with-body stops each command on an HTTP error.
Complete cURL walkthrough: inline base64
JavaScript / TypeScript
Python
Retry and duplicate safety
- Use a unique idempotency key for each intended voice note.
- Reuse the same key only for the same logical send and identical request.
- If a request times out, do not generate a new key merely to retry it.
- If the outcome is ambiguous, inspect the message status and your controlled recipient before attempting another send.
- A new key represents a new send and can create a duplicate voice note.
- If staging expires or the line restarts, stage again. Then make a deliberate send decision while preserving the original key for the same logical send.
voice_note_id returns HTTP 422
with the legacy string code idempotency_collision.
Errors and troubleshooting
POST /api/v1/voice-notes uses the standard error envelope:
POST /api/v1/send uses its legacy flat failure shape:
/send failures do not include trace_id. On 5xx responses, the route may
replace reason and recipient IDs with a generic message. Always branch on
retryable when it is present.
See Errors for the complete numeric registry and retry guidance.
Receive an inbound voice memo
Inbound and outbound voice notes use different API shapes:
There is no separate endpoint for receiving voice notes. In a verified
production event, an Apple voice memo appeared through the ordinary
attachment/media flow with an
attachment_id, a generic
application/octet-stream MIME type, and no filename in the event metadata.
The authenticated content response supplied the useful .caf filename.
Verified observed format
One controlled Apple voice memo had the following characteristics:
This is a verified example, not a universal format guarantee. Apple devices
and OS versions may produce different metadata or audio encodings. Inspect
the download response, file signature, and media metadata before choosing a
decoder.
Chert preserves and exposes the inbound bytes. It does not automatically
convert recordings to MP3 or WAV, and it does not currently return a
transcript or perform speech-to-text. Applications that need transcription
must download the attachment and submit it to their chosen speech-to-text
system.
See Receiving replies
for a representative webhook and complete cURL, TypeScript, Python,
file,
ffprobe, and FFmpeg examples.
Best practices
- Prefer genuine CAF or uncompressed PCM WAV.
- Keep audio between 0.5 seconds and five minutes.
- Stage immediately before sending.
- Preserve the staging response’s
phone_line_id. - Use one stable idempotency key per intended send.
- Keep secrets in environment variables.
- Avoid logging raw base64.
- Send companion text or links separately.
- Do not blindly retry ambiguous sends.
- Test native rendering on a controlled Apple recipient before a large rollout.
Relationship to attachments
Generic audio attachments and native voice notes are separate workflows:
Inbound Apple voice memos are a third case: they arrive as downloadable
attachment parts in
message.received. The inbound event does not reliably
distinguish a native Apple voice memo from every other audio attachment, so do
not infer provenance from type, MIME type, or filename alone.
Do not put a voice-note ID in attachments[]. See Attachments
for the generic file workflow.

