API Endpoints

Authentication

MethodEndpointDescription
POST/api/auth/device/Request a device code for CLI login
POST/api/auth/device/token/Poll for access token (device-code flow)
POST/api/auth/token/refresh/Refresh an expired access token
POST/api/auth/login/Login with credentials
POST/api/auth/logout/Invalidate tokens
GET/api/auth/user/Get current user info

Identities

All Identity endpoints require Bearer authentication.

MethodEndpointDescription
GET/api/identities/List all Identities for the authenticated user
POST/api/identities/Create a new Identity
GET/api/identities/<uuid>/Get a specific Identity
PUT/api/identities/<uuid>/Update an Identity
DELETE/api/identities/<uuid>/Delete an Identity

Create Identity request

{
  "name": "my-agent"
}

Identity response

{
  "uuid": "abc-123",
  "name": "my-agent",
  "inbox": "my-agent@in.ravi.app",
  "phone": "+15551234567",
  "created_dt": "2026-02-25T10:30:00Z"
}

Email inbox

Requires X-Ravi-Identity header.

MethodEndpointDescription
GET/api/email-inbox/List email threads
GET/api/email-inbox/<thread-id>/Get a specific thread with messages

Query parameters: unread=true

Email messages

MethodEndpointDescription
GET/api/email-messages/List all email messages
GET/api/email-messages/<id>/Get a specific email message
POST/api/email-messages/compose/Compose and send a new email
POST/api/email-messages/<id>/reply/Reply to an email
POST/api/email-messages/<id>/reply-all/Reply to all recipients

Compose request

{
  "to": "recipient@example.com",
  "subject": "Hello",
  "body": "<p>HTML content</p>",
  "cc": "",
  "bcc": "",
  "attachment_uuids": []
}

Email attachments

MethodEndpointDescription
POST/api/email-attachments/presign/Get a presigned upload URL

The client uploads the file directly to cloud storage using the presigned URL, then includes the returned attachment UUID in the compose request.

SMS inbox

Requires X-Ravi-Identity header.

MethodEndpointDescription
GET/api/sms-inbox/List SMS conversations
GET/api/sms-inbox/<conversation-id>/Get a specific conversation

Query parameters: unread=true

SMS messages

MethodEndpointDescription
GET/api/messages/List all SMS messages
GET/api/messages/<id>/Get a specific SMS message

Passwords

Requires X-Ravi-Identity header. All password fields are E2E-encrypted ("e2e::<base64>").

:::note[E2E encryption — CLI only] The e2e:: prefix means the field value is end-to-end encrypted with the identity’s PIN-derived key. The server never sees plaintext values. This means:

  • You cannot generate valid e2e:: ciphertext without the user’s PIN. Encrypted fields must be written via the CLI (ravi passwords create) or the Ravi MCP server — never constructed manually.
  • GET responses return ciphertext. Only the CLI (or MCP server running locally) can decrypt them. Direct REST callers cannot read these values headlessly.
  • Use the CLI subprocess pattern or MCP server for vault operations in automated environments. :::
MethodEndpointDescription
GET/api/passwords/List all password entries
POST/api/passwords/Create a new password entry
GET/api/passwords/<uuid>/Get a specific entry (with ciphertext)
PUT/api/passwords/<uuid>/Update a password entry
DELETE/api/passwords/<uuid>/Delete a password entry
GET/api/passwords/generate_password/Generate a random password

Create request (with encrypted fields)

{
  "domain": "example.com",
  "username": "e2e::<base64>",
  "password": "e2e::<base64>",
  "notes": "e2e::<base64>"
}

Vault secrets

Requires X-Ravi-Identity header. Secret values are E2E-encrypted.

MethodEndpointDescription
GET/api/vault/List all secrets (values redacted)
POST/api/vault/Create or update a secret
GET/api/vault/<uuid>/Get a specific secret
DELETE/api/vault/<uuid>/Delete a secret

Encryption

MethodEndpointDescription
GET/api/encryption/Get encryption metadata (salt, public key, verifier)
POST/api/encryption/Upload public key and verifier after first-time PIN setup

Phone

MethodEndpointDescription
GET/api/phone/Get phone numbers for the active Identity

Events (SSE)

MethodEndpointDescription
GET/api/events/stream/Server-Sent Events stream for real-time email and SMS

Requires Authorization: Bearer <token> and X-Ravi-Identity: <uuid> headers.

Supports Last-Event-ID header for resuming after disconnection. The server sends keepalive events every 30 seconds.

SSE event types

The stream emits two named event types. Each event’s data field is a JSON object.

email event — fires when a new email arrives in the identity’s inbox:

{
  "thread_id": "thread-abc123",
  "from_email": "sender@example.com",
  "subject": "Your verification code",
  "preview": "Your code is 847291. It expires in..."
}

sms event — fires when an inbound SMS is received:

{
  "conversation_id": "conv-xyz789",
  "from_number": "+15551234567",
  "preview": "Your verification code is 847291"
}

Keepalive — a comment line (: keepalive) is sent every 30 seconds to prevent proxy timeouts. No action required.

TypeScript example:

import { EventSource } from "eventsource"; // npm install eventsource

const es = new EventSource("https://ravi.app/api/events/stream/", {
  headers: {
    Authorization: `Bearer ${token}`,
    "X-Ravi-Identity": identityUuid,
  },
});

es.addEventListener("sms", (e) => {
  const { from_number, preview } = JSON.parse(e.data);
  const otp = preview.match(/\b(\d{4,8})\b/)?.[1];
  if (otp) handleOtp(otp);
});

es.addEventListener("email", (e) => {
  const { thread_id, subject } = JSON.parse(e.data);
  console.log(`New email: ${subject} (thread ${thread_id})`);
});

SSE vs. polling: Use SSE for agents that need sub-second reaction time (interactive flows). Use polling (GET /api/sms-inbox/?unread=true) for batch scripts and CI — simpler to implement and sufficient for most verification workflows.

Billing

MethodEndpointDescription
GET/api/subscription/Get current subscription status

Returns 402 on endpoints that require an active subscription.

Error reference

All endpoints return standard HTTP status codes. Error responses include a JSON body with a detail field (and sometimes code).

StatusMeaningCommon causeRecovery
400Bad requestMissing required field, invalid formatCheck request body against the schema above
401UnauthorizedMissing or expired Bearer tokenRefresh via POST /api/auth/token/refresh/ or re-authenticate
402Payment requiredAction requires an active Ravi subscriptionUpgrade at ravi.app/billing
403ForbiddenToken valid but missing scope, or identity not owned by userVerify X-Ravi-Identity UUID belongs to the authenticated account
404Not foundUUID does not exist or was deletedRe-list the resource to confirm it exists
409ConflictIdentity name or email already takenChoose a different name or check existing identities
429Rate limitedToo many requestsBack off and retry; email send limits are 60/hr and 500/day
500Server errorUnexpected server-side failureRetry with exponential backoff; report persistent errors

Error response shape

{
  "detail": "Authentication credentials were not provided.",
  "code": "not_authenticated"
}

For validation errors (400), the body may contain field-level detail:

{
  "name": ["This field is required."]
}

Token refresh

When a 401 is returned with "code": "token_expired", refresh the access token:

curl -s -X POST https://ravi.app/api/auth/token/refresh/ \
  -H "Content-Type: application/json" \
  -d '{"refresh": "<refresh_token>"}' \
  | jq .access_token

The new access token is returned as access_token in the response. Store it and retry the original request.