Best Practices

Follow these best practices to build reliable, low-latency integrations with the AgentPhone API.

Voice latency

Use streaming (NDJSON) responses for voice webhooks

Streaming is the single biggest improvement you can make to perceived voice latency. Instead of making the caller wait in silence for your full response, send an interim chunk immediately and let TTS start speaking while your server continues processing.

  • Always use NDJSON streaming for voice webhooks. Return Content-Type: application/x-ndjson so TTS can start on the first chunk.
  • Send an interim filler chunk like “Let me check on that” before calling your LLM or external API. The caller hears natural speech instead of silence.
  • Stream LLM tokens as they arrive rather than waiting for the complete response. Each NDJSON line with "interim": true is spoken immediately.
  • Keep webhook response time under 5 seconds. If processing takes longer, stream an interim acknowledgement within the first second.

See the Calls guide for the full streaming response format and handler examples.

Security

  • Never expose API keys in client-side code or public repositories. Use environment variables.
  • Always verify webhook signatures using the X-Webhook-Signature and X-Webhook-Timestamp headers to ensure requests are from AgentPhone.
  • Check the timestamp — reject requests older than 5 minutes to prevent replay attacks.
  • Use HTTPS for all webhook endpoints in production.
  • Rotate API keys periodically and revoke unused keys.

See Webhooks > Security for verification code examples.

Webhook reliability

  • Return 200 OK quickly — process webhooks asynchronously if your handler needs to do heavy work. Slow responses trigger retries.
  • Handle duplicate deliveries — use the X-Webhook-ID header for idempotency. Retries can cause the same event to be delivered multiple times.
  • Implement retry logic — webhooks are automatically retried on failure (up to 6 attempts over ~24 hours), but handle transient failures gracefully on your end.
  • Log all webhook deliveries for debugging and audit purposes. Use GET /v1/webhooks/deliveries to monitor delivery status.

Pagination

Most list endpoints support offset-based pagination with limit and offset parameters. Message endpoints use cursor-based pagination with before and after timestamps.

Offset-based (agents, numbers, conversations, calls)

GET /v1/numbers?limit=20&offset=0
ParameterDescription
limitNumber of results per page (default: 20, max: 100)
offsetNumber of results to skip (default: 0)

Cursor-based (messages)

GET /v1/numbers/:id/messages?limit=50&before=2025-01-15T12:00:00Z
ParameterDescription
limitNumber of results per page (default: 50, max: 200)
beforeISO 8601 timestamp — get messages before this time
afterISO 8601 timestamp — get messages after this time

Response format

1{
2 "data": [...],
3 "hasMore": true,
4 "total": 42
5}

Always check hasMore before fetching the next page. Use cursor-based pagination for time-ordered data as it’s more efficient than offset-based.

Performance

  • Use webhooks instead of polling for real-time updates.
  • Use cursor-based pagination (before/after) for loading message history.
  • Cache frequently accessed data like phone number lists.
  • Respect rate limits — implement exponential backoff for retries. Check the Retry-After header on 429 responses.

Error handling

  • Always check response status before processing data.
  • Implement retry logic for transient errors (429, 500, 502).
  • Log errors with context for debugging.
  • Handle validation errors gracefully — check the details array for field-level errors.

See the Error Handling page for the full error response format and code examples.