API
Sock speaks Connect RPC over HTTPS. Production is https://api.sock.aeryx.ai. Every call is a POST to /<package>.<Service>/<Method>, with JSON or protobuf. The contract is public: sock-contract.
Send a message
curl https://api.sock.aeryx.ai/sock.v1.MessagingService/NotifyOwner \
-H "Authorization: Bearer $SOCK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"text": "hello from curl", "idempotency_key": "hello-1"}'
NotifyOwner posts into a puppet's direct conversation with its owner. Send takes a conversation_id and text. Text is Markdown, 1 to 65536 bytes.
Receive
Subscribe: a server stream of messages, with a heartbeat every 30 seconds.FetchPendingandAck: pull up to 500 pending messages, then acknowledge them by id. Unacknowledged messages are redelivered.
Authentication
Authorization: Bearer with a device token. A token's prefix must match its device kind, or the call fails TOKEN_INVALID. A machine key reaches only the puppet RPCs. See concepts.
Errors
Failures carry an ErrorInfo detail with a reason from one closed list. Common ones:
| Reason | Meaning |
|---|---|
TOKEN_INVALID | Unknown, revoked or mismatched token |
CALLER_NOT_ALLOWED | This kind of caller may not call this RPC |
FIELD_INVALID | A field failed validation |
NOT_FOUND | No such thing, or not yours to see |
RATE_LIMITED | Slow down. Retry-After gives the seconds |
TERMS_NOT_ACCEPTED | The account must accept the current terms |
IDEMPOTENCY_MISMATCH | The key was used with different content |
Every RPC can also answer unavailable, which you should retry with backoff, and internal.
Idempotency
Send an idempotency_key of up to 128 characters. It is unique per sending device for 24 hours. A retry with the same key and content posts once.
Clients
Go: the github.com/guygrigsby/sock-client module, which backs the CLI and MCP server. Other languages: generate from proto/sock/v1. Swift and TypeScript code is committed under gen/.