summaryrefslogtreecommitdiff
path: root/docs/api.md
blob: 9d803ad5c4e2646fe083726f6121493934fb1f87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# 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",
    }
}
```

## 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.

### `GET /api/channels`

Lists channels.

#### On success

Responds with a list of channel objects, one per channel:

```json
[
    {
        "name": "nonsense and such",
        "id": "C1234abcd",
    }
]
```

### `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.

### `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.

#### 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. If this header is omitted, then the stream will start from the beginning.

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:   "channel": {
data:     "id": "C9876cyyz",
data:     "name": "example channel 2"
data:   }
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:   "message": {
data:     "id": "Mxnjcf3y41prfry9",
data:     "body": "beep"
data:   }
data: }
```