Skip to main content

Overview

The endpoint-api delivers conversation events to external HTTP endpoints. When a call starts, ends, or a message is exchanged, assistant-api emits events to endpoint-api via gRPC. This service handles fan-out to all registered webhooks, retry with exponential backoff, and delivery tracking.

Port

9005 — HTTP · gRPC (cmux)

Language

Go 1.25 Gin (REST) + gRPC

Storage

PostgreSQL endpoint_db Redis (job queue)

Components

Stores webhook configurations scoped to a project. Each webhook targets one URL and subscribes to one or more event types.
FieldTypeDescription
namestringHuman-readable label
urlstringDestination HTTPS endpoint
eventsstring[]Subscribed event types
is_activeboolEnable / disable without deleting
secretstringHMAC signing secret
max_retriesintDefault: 5
timeout_msintDefault: 30000
headersJSONCustom headers sent with each delivery
When assistant-api calls endpoint-api with an event, the service:
  1. Resolves all active webhooks for the project that subscribe to the event type.
  2. Creates a delivery record per webhook.
  3. Signs the payload with HMAC-SHA256 using the webhook’s secret.
  4. Attempts delivery with the configured timeout.
  5. On failure: re-queues via Redis with exponential backoff.
AttemptDelay
1Immediate
25 seconds
325 seconds
42 minutes
510 minutes
Retries are triggered for HTTP status 408, 429, and 5xx. Status codes 2xx are treated as success. Status codes 4xx (except 408, 429) are treated as non-retriable failures.
Every delivery includes an X-Rapida-Signature header:
X-Rapida-Signature: sha256=<hmac-sha256-hex>
Verification example (Node.js)
const crypto = require('crypto');

function verifySignature(rawBody, signatureHeader, secret) {
  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signatureHeader),
    Buffer.from(expected)
  );
}

Event Reference

EventTriggerKey Payload Fields
conversation.startedCall beginsconversation_id, assistant_id, timestamp
conversation.endedCall terminatesconversation_id, duration_ms, messages_count, end_reason
message.sentAssistant speaksconversation_id, message_id, content, role
message.receivedUser speaksconversation_id, message_id, content, role
assistant.updatedConfig changedassistant_id, changes
assistant.deletedAssistant removedassistant_id, timestamp
error.occurredPipeline errorconversation_id, error_code, error_message
Example payload — conversation.ended
{
  "id": "evt_1234567890abcdef",
  "type": "conversation.ended",
  "timestamp": 1704067200000,
  "data": {
    "conversation_id": "conv_abc123",
    "assistant_id": "asst_def456",
    "duration_ms": 125000,
    "messages_count": 8,
    "end_reason": "user_hangup"
  }
}

API Endpoints

MethodPathDescription
GET/readiness/Readiness probe
GET/healthz/Liveness probe
POST/api/v1/endpoint/webhooksCreate webhook
GET/api/v1/endpoint/webhooksList webhooks
GET/api/v1/endpoint/webhooks/{id}Get webhook
PUT/api/v1/endpoint/webhooks/{id}Update webhook
DELETE/api/v1/endpoint/webhooks/{id}Delete webhook
POST/api/v1/endpoint/webhooks/{id}/testSend test event
GET/api/v1/endpoint/webhooks/{id}/deliveriesList delivery history
POST/api/v1/endpoint/webhooks/{id}/replayReplay a specific event
Create a webhook
curl -X POST http://localhost:9005/api/v1/endpoint/webhooks \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "CRM Sync",
    "url": "https://crm.example.com/webhooks/rapida",
    "events": ["conversation.ended", "message.sent"],
    "headers": { "Authorization": "Bearer crm-api-key" },
    "max_retries": 5,
    "timeout_ms": 30000
  }'
Test a webhook
curl -X POST http://localhost:9005/api/v1/endpoint/webhooks/{id}/test \
  -H "Authorization: Bearer <jwt>"

Configuration

Edit docker/endpoint-api/.endpoint.env before starting the service.

Required variables

VariableRequiredDefaultDescription
SECRET✅ Yesrpd_pksJWT signing secret — must match all services
POSTGRES__HOST✅ YespostgresPostgreSQL host
POSTGRES__DB_NAME✅ Yesendpoint_dbDatabase name
POSTGRES__AUTH__USER✅ Yesrapida_userDatabase user
POSTGRES__AUTH__PASSWORD✅ YesDatabase password
REDIS__HOST✅ YesredisRedis host (job queue for retries)

Tuning variables

VariableDefaultDescription
LOG_LEVELdebugdebug · info · warn · error
WEBHOOK_MAX_RETRIES5Maximum delivery attempts per event
WEBHOOK_RETRY_DELAY5000Initial retry delay in milliseconds
WEBHOOK_TIMEOUT30000HTTP call timeout in milliseconds

Full environment file

# ── Service identity ──────────────────────────────────────────────
SERVICE_NAME=endpoint-api
HOST=0.0.0.0
PORT=9005
LOG_LEVEL=debug
SECRET=rpd_pks
ENV=development

# ── PostgreSQL ────────────────────────────────────────────────────
POSTGRES__HOST=postgres
POSTGRES__PORT=5432
POSTGRES__DB_NAME=endpoint_db
POSTGRES__AUTH__USER=rapida_user
POSTGRES__AUTH__PASSWORD=rapida_db_password
POSTGRES__MAX_OPEN_CONNECTION=15
POSTGRES__MAX_IDEAL_CONNECTION=8
POSTGRES__SSL_MODE=disable

# ── Redis (retry job queue) ───────────────────────────────────────
REDIS__HOST=redis
REDIS__PORT=6379
REDIS__DB=2
REDIS__MAX_CONNECTION=10

# ── Webhook delivery settings ─────────────────────────────────────
WEBHOOK_MAX_RETRIES=5
WEBHOOK_RETRY_DELAY=5000
WEBHOOK_TIMEOUT=30000

Running

make up-endpoint

make logs-endpoint

make rebuild-endpoint

Health & Observability

EndpointPurpose
GET /readiness/Reports whether the service is ready
GET /healthz/Liveness probe
curl http://localhost:9005/readiness/

Troubleshooting

  1. Check the webhook is active: GET /api/v1/endpoint/webhooks/{id}
  2. Verify the event type is in the webhook’s events list.
  3. Review delivery history: GET /api/v1/endpoint/webhooks/{id}/deliveries
  4. Send a test event: POST /api/v1/endpoint/webhooks/{id}/test
  • Verify the destination URL is publicly reachable (not localhost or a private IP).
  • Confirm the TLS certificate on the destination is valid.
  • Check for firewall rules blocking outbound HTTP from the endpoint-api container.
# Test connectivity from inside the container
docker exec endpoint-api curl -v https://your-webhook-endpoint.com
curl -X POST http://localhost:9005/api/v1/endpoint/webhooks/{id}/replay \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{"event_id": "evt_1234567890abcdef"}'

Next Steps