Skip to content
Kelly

Developers

askkelly.aiGet a key

Kelly Wire

Copy page

The live socket: how to connect, authenticate and resume, the control messages, and the ticket and single-topic stream operations.

ConnectLink to this section

ServerURLWhat it is
Kelly Wirewss://wire.askkelly.ai/v1Kelly Wire. Browsers connect from an allowed origin: first-party tickets accept https://askkelly.ai, https://www.askkelly.ai, card tickets the card origins and *.claudemcpcontent.com. A ticket from any other origin closes with 4013.
Server-sent eventshttps://api.askkelly.ai/v1/streamThe SSE alternative on the API host, for widgets and cards whose frames cannot open a socket.

The protocolLink to this section

Live topics over one WebSocket at wss://wire.askkelly.ai/v1, subprotocol kelly.v1. Every frame is one JSON object. Data messages (snapshot and delta) use one envelope, {op, type, topic, seq, source_ts, emit_ts, source, data}; control messages carry op and their own members. Clients ignore unknown op and type values and unknown members.

Authenticate with a single-use ticket of 60 seconds or less in the first message, within 5 seconds; a browser may offer it as a second subprotocol instead. Long-lived keys never go in a URL. The credential that minted the ticket decides the surface, never the host.

Each topic sends a snapshot with its last_seq, then deltas at last_seq + 1. Subscribing to a topic already held sends its snapshot again. Gap rule: Any sequence jump or checksum mismatch on a topic means: drop that topic's state and fetch a new snapshot, then apply deltas from its last_seq + 1.

The server pings every 20 seconds; two missed pings mean the connection is dead. A resume within 120 seconds sends the session ID, the resume token and the last seq per topic, needs no new ticket and replays missed deltas; topics outside the window get resume_failed and a fresh snapshot. A connection lives 24 hours at most and ends with a drain notice 30 seconds ahead.

Close codes: 4000–4099 Fatal for this request: do not reconnect unchanged.; 4100–4199 Back off: reconnect after at least 1 second, with exponential backoff and jitter.; 4200–4299 Reconnect now and resume immediately..

Every snapshot carries expected_latency (seconds, basis and source) and liveness, and a feed message says when either changes. Nothing claims to be faster than its feed.

Per-book prices reach signed-in first-party tickets only (D4); no edge travels on a shared topic (D21); every member passes the rights register field by field. Off the site, topics carry engine-computed values only.

Tickets and single-topic streamsLink to this section

MethodPathTitleAccessPriceMCP tool
POST/v1/wire/ticketsGet a Kelly Wire ticketpublic[object Object]none
GET/v1/stream/{topic}Stream one topic as server-sent eventspublic[object Object]none

POST /v1/wire/tickets

Get a Kelly Wire ticketLink to this section

public, no key needed · [object Object] credits, not charged during the beta · status live

Mints a single-use ticket that lives 60 seconds, for the socket (wss://wire.askkelly.ai/v1, sent in the first message) or for one stream (GET /v1/stream/{topic}?ticket=). Needs an API key with the wire:connect scope, or an OAuth token. Stream tickets name their topics; socket tickets may name some (bound to them) or none. Each topic is still checked against the plan when it is opened.

ParametersLink to this section

Body, application/json:

transportstring

ws for the socket (wss://…/v1), sse for GET /v1/stream/{topic}. A ticket works on the transport it was minted for only.

One of: ws, sse (open: new values may be added).

default "ws"

topicsarray of stringnullable

Topics the ticket is bound to, such as game:gm_… or market:mkt_…; required for sse. At most the plan's topic limit. Omit on a socket ticket to subscribe to any topic the plan allows.

max items 5000

modestringnullable

test serves the recorded Sunday (sample data) to any credential; live is refused for a test-mode key. Omitted: the credential's own mode.

One of: live, test (open: new values may be added).

ResponseLink to this section

201 The ticket. It is shown once; responses are never cached.

objectconstrequired

Always wire_ticket.

Always "wire_ticket".

default "wire_ticket"

ticketstringrequired

The ticket (wtkt_…); single use, 60 seconds or less.

pattern ^wtkt_[0123456789abcdefghjkmnpqrstvwxyz]{20}$

expires_atstringrequired

When the ticket stops working, unused.

format date-time

topicsarray of stringrequirednullable

The topics it is bound to; null for any topic the plan allows.

transportstringrequired

The transport it works on.

One of: ws, sse (open: new values may be added).

modestringrequired

live data, or test: the recorded Sunday.

One of: live, test (open: new values may be added).

urlstringrequired

Where to connect: the socket's URL, or the stream's URL (its {topic} filled in when the ticket names one topic). It never contains the ticket.

ErrorsLink to this section

invalid_request · credential_in_url · unsupported_api_version · unauthenticated · method_not_allowed · rate_limited · internal_error · service_unavailable · quota_exhausted · insufficient_scope · not_entitled · payload_too_large · too_many_topics

ExampleLink to this section

Request
POST https://api.askkelly.ai/v1/wire/tickets
Authorization: Bearer $KELLY_API_KEY
Content-Type: application/json

{ "transport": "sse", "topics": [ "game:gm_hwxy9tewzag7fee8hb01" ], "mode": "test" }

Response, 403
{
  "type": "https://docs.askkelly.ai/problems/insufficient_scope",
  "title": "Insufficient scope",
  "status": 403,
  "code": "insufficient_scope",
  "detail": "This needs the wire:connect scope.",
  "param": "wire:connect",
  "doc_url": "https://docs.askkelly.ai/problems/insufficient_scope",
  "request_id": "req_ecea15c85392623ab2018a24",
  "retryable": false,
  "display_message": "This app needs one more permission to do that."
}
Request
POST https://api.askkelly.ai/v1/wire/tickets
Authorization: Bearer $KELLY_API_KEY
Content-Type: application/json

{ "transport": "sse", "topics": [ "game:gm_hwxy9tewzag7fee8hb01" ], "mode": "test" }

Response, 201
{
  "object": "wire_ticket",
  "ticket": "wtkt_nj7ftwgg0hbac03jg4wb",
  "expires_at": "2026-10-04T17:31:00Z",
  "topics": [
    "game:gm_hwxy9tewzag7fee8hb01"
  ],
  "transport": "sse",
  "mode": "test",
  "url": "https://api.askkelly.ai/v1/stream/game:gm_hwxy9tewzag7fee8hb01"
}

GET /v1/stream/{topic}

Stream one topic as server-sent eventsLink to this section

public, no key needed · [object Object] credits, not charged during the beta · status live

One topic as server-sent events (STR-21), for widgets, simple clients and cards whose host blocks sockets. Each event's id is the topic's seq: first event: snapshot (the topic's state, its last seq, expected latency and liveness), then one event per change, named by its type (game.state, market.update, market.suspended, ...), each seq the previous + 1. Send Last-Event-ID to resume: missed changes replay while they are within the 120-second window, else a fresh snapshot comes first. A gap is answered with a fresh snapshot. A comment line pings every 20 seconds; event: drain comes 30 seconds before the stream's 24-hour limit, and event: error (a catalogue code) when the topic is no longer available, after which the stream ends. Off askkelly.ai streams carry engine-computed values only. Each open stream costs wire_topic_hour credits when it opens and each hour it stays open.

ParametersLink to this section

topicstring · pathrequired

The topic, such as game:gm_… or market:mkt_… (the Wire topics in the AsyncAPI document).

pattern ^[a-z]+:[A-Za-z0-9_\-/:]+$ · max length 200

ticketstring · query

A ticket minted for sse that names this topic (POST /v1/wire/tickets): single use, 60 seconds or less (STR-24). The one credential a URL may carry; never an API key. Omit it on askkelly.ai, where the session is used.

pattern ^wtkt_[0123456789abcdefghjkmnpqrstvwxyz]{20}$

last_event_idstring · query

The same as Last-Event-ID, for a new EventSource, which can't set the header; the header wins when both are sent.

pattern ^[0-9]{1,18}$

Last-Event-IDstring · header

The last event id received (a seq), to resume after a reconnect.

pattern ^[0-9]{1,18}$

ResponseLink to this section

200 The stream. Events: snapshot, one per change (named by its type), drain, error and warning; comment lines are pings.

text/event-stream: Server-sent events; each data line is one Kelly Wire envelope.

ErrorsLink to this section

invalid_request · credential_in_url · unsupported_api_version · unauthenticated · method_not_allowed · rate_limited · internal_error · service_unavailable · quota_exhausted · insufficient_scope · not_entitled · not_found · too_many_topics

Control messagesLink to this section

The socket itself: authentication, subscriptions, heartbeats, drains and resumes. Connect to wss://wire.askkelly.ai/v1 with the subprotocol kelly.v1. Every topic below is subscribed to by name on this one socket.

authLink to this section

You send. The first message: a ticket minted over authenticated REST (POST /v1/wire/tickets with a key or token, POST /me/wire/tickets on askkelly.ai, or get_wire_ticket for a card), within the authentication deadline (STR-6). Browsers may instead offer the ticket as a second subprotocol, kelly.ticket.{ticket}. Sent again mid-session with a new ticket and a request_id, it refreshes the credential in-band (STR-22) and is answered by an ack. Long-lived keys never go in a URL.

Show 3 fields
opconstrequired

Always auth.

Always "auth".

ticketstringrequired

The ticket (wtkt_…), single-use, living 60 seconds or less.

request_idstring

Only on an in-band refresh; echoed in the ack.

helloLink to this section

Kelly sends. Sent after authentication or a resume: the heartbeat interval, the session ID and resume token, the resume window, when the connection's lifetime ends and the published limits.

Show 9 fields
opconstrequired

Always hello.

Always "hello".

session_idstringrequired

Handle for resuming this session (wses_…).

resume_tokenstringrequired

Secret that proves a resume comes from this session; a new one in every hello.

heartbeat_secondsintegerrequired

Seconds between server pings.

min 0

resume_secondsintegerrequired

Seconds of per-topic history kept for a resume.

min 0

expires_atstringrequired

RFC 3339 time the 24-hour lifetime ends; a drain notice comes first.

format date-time

limitsanyrequired

topics, inbound_per_second and max_frame_bytes for this connection (STR-14).

modestringrequired

live, or test for test-mode credentials (the recorded Sunday).

One of: live, test (open: new values may be added).

rg_linestringrequired

The cost line (COMP-1, D19): 21+ and the helpline, from brand.toml [cost_line]. Show it with anything built on a wagering topic; every wagering topic's snapshot carries it too.

subscribeLink to this section

You send. Adds topics. Entitlement is checked per topic (STR-7). Subscribing to a topic already held sends its snapshot again (a resync).

Show 3 fields
opconstrequired

Always subscribe.

Always "subscribe".

request_idstring

Echoed in the ack.

topicsarray of stringrequired

Topics to add.

unsubscribeLink to this section

You send. Removes topics.

Show 3 fields
opconstrequired

Always unsubscribe.

Always "unsubscribe".

request_idstring

Echoed in the ack.

topicsarray of stringrequired

Topics to remove.

list_subscriptionsLink to this section

You send. Asks for the current topic set.

Show 2 fields
opconstrequired

Always list_subscriptions.

Always "list_subscriptions".

request_idstring

Echoed in the ack.

ackLink to this section

Kelly sends. Answers subscribe, unsubscribe, list_subscriptions and an in-band auth, echoing the full current topic set (STR-8).

Show 3 fields
opconstrequired

Always ack.

Always "ack".

request_idstring

The request this answers.

topicsarray of stringrequired

The full current topic set.

feedLink to this section

Kelly sends. A topic's expected latency or liveness changed: a poller paused when its budget ran out (STR-5), or no update arrived within the topic's limit, so it is now stale.

Show 4 fields
opconstrequired

Always feed.

Always "feed".

topicstringrequired

The topic.

expected_latencyany

seconds, basis and source.

livenessany

state, as_of and limit_seconds.

errorLink to this section

Kelly sends. A per-topic or per-request error with a catalogue code, such as not_entitled (STR-7, STR-22).

Show 6 fields
opconstrequired

Always error.

Always "error".

codestringrequired

A code from the error catalogue.

detailstring

Prose; may change.

topicstring

The topic it concerns, if any.

request_idstring

The request it answers.

retryableboolean

Whether retrying can succeed.

warningLink to this section

Kelly sends. The connection is falling behind: read faster or drop topics, or it closes with 4108 (STR-13).

Show 3 fields
opconstrequired

Always warning.

Always "warning".

codestringrequired

slow_consumer.

detailstring

Prose; may change.

pingLink to this section

Kelly sends. Heartbeat: answer with pong. Two missed pings mean the connection is dead (STR-12).

Show 2 fields
opconstrequired

Always ping.

Always "ping".

tsstringrequired

RFC 3339 server time.

pongLink to this section

You send. Answers a ping.

Show 2 fields
opconstrequired

Always pong.

Always "pong".

tsstring

The ping's ts, echoed.

drainLink to this section

Kelly sends. The server will close this connection: open a new one and resume before the deadline (STR-16, STR-23).

Show 2 fields
opconstrequired

Always drain.

Always "drain".

deadline_atstringrequired

RFC 3339 time the connection closes.

format date-time

resumeLink to this section

You send. The first message of a reconnect, instead of auth: the session ID, its resume token and the last seq seen per topic (STR-11). Inside the resume window it needs no new ticket.

Show 4 fields
opconstrequired

Always resume.

Always "resume".

session_idstringrequired

From hello.

resume_tokenstringrequired

From the latest hello.

last_seqobjectrequired

Topic → last seq seen.

resume_failedLink to this section

Kelly sends. Some or all topics fell outside the replay window: drop their state; a snapshot for each follows (STR-11). When the session itself has expired, the connection then closes with 4011 and the client needs a new ticket.

Show 2 fields
opconstrequired

Always resume_failed.

Always "resume_failed".

topicsarray of stringrequired

Topics that need a new snapshot.

Generated from Kelly Wire 2026-10-05 and Kelly API 2026-10-05.

Support: support@askkelly.ai · Security and disclosure