# the hi api ## The basics The `hi` API is exposed as HTTP endpoints that accept JSON and return JSON on success, with a few exceptions noted below. On errors, the response body is freeform text and is meant to be shown to the user, logged, or otherwise handled. Programmatic action should rely on the status code, as documented. Requests that require a JSON body must include a `content-type: application/json` header. For requests that take a JSON body, if the body does not match the required schema, the endpoint will return a 422 Unprocessable Entity response, instead of the responses documented for that endpoint. ## Client initialization Clients will generally need some information about the session in order to present a coherent view to the user, including the session's login identity. ### `GET /api/boot` Returns information needed to boot the client. Also the recommended way to check whether the current `identity` cookie is valid, and what login it authenticates. #### On success ```json { "login": { "name": "example username", "id": "L1234abcd", }, "resume_point": "1312", "channels": [ { "name": "nonsense and such", "id": "C1234abcd", "messages": [ { "at": "2024-09-27T23:19:10.208147Z", "sender": { "id": "L1234abcd", "name": "example username" }, "id": "M1312acab", "body": "beep" } ] } ] } ``` ## Authentication Other than where noted below, all endpoints require authentication. To authenticate a request, send a `cookie: identity=YOUR TOKEN HERE` header in the request. Tokens can be obtained via the `/api/auth/login` endpoint. This authentication protocol is intended to integrate with browsers and browser-like environments, where the browser handles cookie headers automatically. If the token is not valid or has expired, then `hi` will send back a 401 Unauthorized response, instead of the responses documented for the endpoint the request was intended for. ### `POST /api/auth/login` Authenticates the user by login name and password, creating a login if none exists. **This endpoint does not require an `identity` cookie.** #### Request ```json { "name": "example username", "password": "the plaintext password", } ``` #### On success This endpoint returns a 204 No Content response on success, with a `Set-Cookie` header setting the `identity` cookie to a newly created token for this login. This cookie must be presented in future requests, and will authenticate the associated login. The cookie will expire if it is not used regularly. (As of this writing, identity cookies expire seven days after their last use, but this time period may change.) #### Authentication failures If the login already exists, and the provided password is different from the one used to create the login, then this will return a 401 Unauthorized response. ### `POST /api/auth/logout` Invalidates the identity token, logging the user out. #### Request ```json {} ``` #### On success This endpoint returns a 204 No Content response on success, with a `Set-Cookie` header that clears the `identity` cookie. The cookie provided in the request is also invalidated, and will not authenticate future requests even if the client fails to process the `Set-Cookie` response header. ## Working with channels Channels are the containers for conversations. The API supports listing channels, creating new channels, and send messages to an existing channel. ### `POST /api/channels` Creates a channel. #### Request ```json { "name": "a unique channel name" } ``` #### On succeess ```json { "name": "a unique channel name", "id": "C9876cyyz" } ``` #### On duplicate channel name Channel names must be unique. If a channel with the same name already exists, this will return a 400 Bad Request error. ## Events The API delivers events to clients to update them on other clients' actions and messages. While there is no specific delivery deadline, messages are delivered as soon as possible on a best-effort basis, and the event system allows clients to replay events or resume interrupted streams, to allow recovery if a message is lost. ### `POST /api/channels/:channel` Sends a chat message to a channel. It will be relayed to clients subscribed to the channel's events, and recorded for replay. The `:channel` placeholder must be a channel ID, as returned by `GET /api/channels` or `POST /api/channels`. Chat messages expire after 90 days and can no longer be retrieved at that time. #### Request ```json { "message": "my amazing thoughts, by bob" } ``` #### On success Once the message is accepted, this will return a 202 Accepted response. The message will be delivered to subscribers asynchronously, as soon as is feasible. #### Invalid channel ID If the channel ID is not valid, this will return a 404 Not Found response. ### `DELETE /api/channels/:channel` Deletes a channel (and all messages in it). The `:channel` placeholder must be a channel ID, as returned by `GET /api/channels` or `POST /api/channels`. #### On success This will return a 202 Accepted response on success, and delete the channel. #### Invalid channel ID If the channel ID is not valid, this will return a 404 Not Found response. ### `DELETE /api/messages/:message` Deletes a message. The `:message` placeholder must be a message ID, as returned from the event stream or from a list of messages. #### On success This will return a 202 Accepted response on success, and delete the message. #### Invalid message ID If the message ID is not valid, this will return a 404 Not Found response. ### `GET /api/events` Subscribes to events. This endpoint returns an `application/event-stream` response, and is intended for use with the `EventSource` browser API. Events will be delivered on this stream as they occur, and the request will remain open to deliver events. The returned stream may terminate, to limit the number of outstanding messages held by the server. Clients can and should repeat the request, using the `Last-Event-Id` header to resume from where they left off. Events will be replayed from that point, and the stream will resume. #### Query parameters This endpoint accepts an optional `resume_point` query parameter. If provided, the value must be the value obtained from the `/api/boot` method. This parameter start the returned stream immediately after the `resume_point`. #### Request headers This endpoint accepts an optional `Last-Event-Id` header for resuming an interrupted stream. If this header is provided, it must be set to the `id` field sent with the last event the client has processed. When `Last-Event-Id` is sent, the response will resume immediately after the corresponding event. This header takes precedence over the `resume_point` query parameter; if neither is provided, then event playback starts at the beginning of time (_you have been warned_). If you're using a browser's `EventSource` API, this is handled for you automatically. The event IDs `hi` sends in `application/event-stream` encoding are ephemeral, and can only be reused within the brief intervals required to reconnect to the event stream. Do not store them, and do not parse them. The message data's `"id"` field is the durable identifier for each message. #### On success The returned event stream is a sequence of events: ```json id: 1233 data: { data: "type": "created", data: "at": "2024-09-27T23:18:10.208147Z", data: "id": "C9876cyyz", data: "name": "example channel 2" data: } id: 1234 data: { data: "type": "message", data: "at": "2024-09-27T23:19:10.208147Z", data: "channel": { data: "id": "C9876cyyz", data: "name": "example channel 2" data: }, data: "sender": { data: "id": "L1234abcd", data: "name": "example username" data: }, data: "id": "M1312acab", data: "body": "beep" data: } id: 1235 data: { data: "at": "2024-09-28T02:44:27.077355Z", data: "channel": { data: "id": "C9876cyyz", data: "name": "example channel 2" data: }, data: "type": "message_deleted", data: "id": "M1312acab" data: } id: 1236 data: { data: "at": "2024-09-28T03:40:25.384318Z", data: "type": "deleted", data: "id": "C9876cyyz" data: } ```