on cd863bb help sign in
urbit.org/content/docs/userspace/http-api-guide.md
698 lines 34 kB history raw
1+++
2title = "HTTP API Guide"
3weight = 4
4template = "doc.html"
5+++
7[Eyre] is the web-server [vane] (kernel module) of [Arvo], an Urbit ship's
8kernel and operating system. In this guide, we'll look at interacting with a
9ship through Eyre's web API using the [@urbit/http-api] Javascript module.
11## Background
13Before we dig into the details of Eyre's API, we'll first give a quick run-down
14of some concepts in Arvo. Eyre's API is a fairly thin overlay on some of Arvo's
15internal systems, so there's some basic things to understand.
17### Clay
19[Clay] is the filesystem vane. It's typed, and it's revision-controlled in a
20similar way to git. Clay is comprised of a number of [desk]s, which are a bit
21like git repositories. Each app on your ship's home screen corresponds to a desk
22in Clay. That desk contains the source code and resources for that app.
24#### Marks
26Most of Clay's workings aren't relevant to front-end development, but there's
27one important concept to understand: [mark]s. Clay is a typed filesystem, and
28marks are the filetypes. There's a mark for `.hoon` files, a mark for `.txt`
29files, and so on. The mark specifies the hoon datatype for those files, and it
30also specifies conversion methods between different marks. Marks aren't just
31used for files saved in Clay, but also for data that goes to and from the web
32through Eyre.
34When you send a [poke](#pokes) or run a [thread](#threads) through Eyre, it will
35look at the mark specified (for example `graph-action-3`, `settings-event`, etc)
36and use the corresponding mark file in the desk to convert the given JSON to
37that datatype before passing it to the target [agent](#gall-agents) or thread.
38The same conversion will happen in reverse for responses.
40Note that Eyre makes a best effort attempt to convert data to and from JSON. If
41the marks in question do not contain appropriate JSON conversion functions, it
42will fail. Not all [scry endpoints](#scry-endpoints), [subscription
43paths](#subscriptions), pokes, etc, are intended to be used from a front-end, so
44not all use marks which can be converted to and from JSON (the `noun` mark for
45example). If documentation is available for the agent or thread in question, it
46should note in some fashion whether it can take or produce JSON. The majority of
47things you'll want to interact with through Eyre will work with JSON.
49### Gall agents
51An _agent_ is a userspace application managed by the [Gall] vane. A desk may
52contain multiple agents that do different things. The Groups app, for example,
53has `graph-store`, `group-store`, `contact-store`, and others in its desk.
54Agents are the main thing you'll interact with through Eyre. They have a simple
55interface with three main parts:
57| Interface | Description |
58| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
59| Pokes | One-off message to an agent. Pokes often represent actions, commands, requests, etc. |
60| Subscriptions | An agent may have a number of different paths to which you may subscribe. If a subscription request is accepted, you'll continue to receive update/events the agent sends out on that path until you either cancel the subscription or are kicked by the agent. |
61| Scries | Scries are one-off requests for data. Like subscriptions, they are organized by path. Scry requests will be fulfilled immediately. Scries are "read-only", they cannot alter the state of an agent. |
63#### Pokes
65Pokes are single, stand-alone messages to agents. Pokes are how you send data
66and requests to agents. Agents will send back either a positive acknowledgement
67(ack) or a negative acknowledgement (nack). The agent can't send actual data
68back in the acks. If they have any response to give back, it will be sent out to
69subscribers on a subscription path instead.
71The pokes an agent accepts will be defined in the `on-poke` section of its
72source in the desk's `/app` directory, and typically in its type definition file
73in the `/sur` directory too. If the agent has documentation, the data structures
74and marks of its pokes should be listed there.
76`http-api` includes a `poke` function which allows you to perform pokes through
77Eyre, and is [detailed below](#poke).
79#### Subscriptions
81Agents define subscription paths which you can subscribe to through Eyre. A path
82might be simple and fixed like `/foo/bar`, or it might have variable elements
83where you can specify a date, ship, or what have you. Each agent will define its
84subscription paths in the `on-watch` section of its source, and if it has
85documentation the paths should also be listed there.
87You can subscribe by sending a watch request to the agent with the desired path
88specifed. The agent will apply some logic (such as checking permissions) to the
89request and then ack or nack it. If acked, you'll be subscribed and begin
90receiving any updates the agent sends out on that path. What you'll receive on a
91given path depends entirely on the agent.
93Agents can kick subscribers, and you can also unsubscribe at any time.
95`http-api` includes a `subscribe` function which allows you to perform pokes through
96Eyre, and is [detailed below](#subscribe).
98#### Scry Endpoints
100Pokes and subscriptions can modify the state of the agent. A third kind of
101interaction called a _scry_ does not - it simply retrieves data from the agent
102without any side-effects. Agents can define _scry endpoints_ which (like
103subscriptions) are paths. A scry to one of these endpoints will retrieve some
104data (as determined by the agent's design) from the agent's state. Like
105subscription paths, scry paths can be simple like `/foo/bar` or contain variable
106elements. Unlike subscriptions, a scry is a one-off request and the data will
107come back immediately.
109Scry endpoints are defined in the `on-peek` section of an agent's source, and
110will likely be listed in the agent's documentation if it has any. Scry endpoints
111will often be written with a leading letter like `/x/foo/bar`. All scries
112through Eyre are `x` scries, so that letter (called a `care`) needn't be
113specified.
115`http-api` includes a `scry` function which allows you to perform scries through
116Eyre, and is [detailed below](#scry).
118### Threads
120A thread is a monadic function in Arvo that takes arguments and produces a
121result. Threads are conceptually similar to Javascript promises - they can
122perform a series of asynchronous I/O operations and may succeed or fail. Threads
123are often used to handle complex I/O operations for agents. Threads live in the
124`/ted` directory of a desk.
126`http-api` includes a `thread` function which allows you to run threads through
127Eyre, and is [detailed below](#thread).
129## `http-api` basics
131The `http-api` module provides convenient functions to interact with a ship
132through Eyre. In this section we'll discuss the basics of how it works.
134### Importing the module
136The `http-api` module is available in npm as `@urbit/http-api`, and can be
137installed with:
139```
140npm i @urbit/http-api
141```
143Once installed, you can import it into your app with:
145```
146import Urbit from '@urbit/http-api';
147```
149Note that the examples in this guide are simple HTML documents with vanilla
150Javascript in `<script>` tags, so they use [unpkg.com](https://unpkg.com) to
151import `@urbit/http-api`. This is not typical, and is just done here for
152purposes of simplicity.
154### `Urbit`
156All functionality is contained within the `Urbit` class. There are two ways to
157instantiate it, depending on whether your web app is served directly from the
158ship or whether it's served externally. The reason for the difference is that
159you require a session cookie to talk to the ship. If your app is served from the
160ship, the user will already have a cookie. If it's not, they'll need to
161authenticate, which is [detailed separately below](#authentication).
163In the case of a front-end served from the ship, the `Urbit` class contains a `constructor` which takes three arguments:
165| Argument | Type | Description | Example |
166| -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
167| `url` | `string` | The host of the ship without the protocol. This is mandatory but would typically be left blank as requests will still work if they're root-relative paths. | `"example.com"`, `"localhost:8080"`, `""` |
168| `code` | `string` | The web login code of the ship. You'll typically use this constructor when it's served directly from the ship and therefore you'll already have a session cookie, so this field is optional and is typically omitted or left empty. | `""`, `"lidlut-tabwed-pillex-ridrup"` |
169| `desk` | `string` | The desk on which you want to run threads. This field is optional and is only used if you want to run threads, no other function uses it. If you're not running threads, you can omit it or leave it empty. | `"landscape"`, `""` |
171To create an `Urbit` instance, you can simply do:
173```javascript
174const api = new Urbit("");
175```
177If you want to specify a desk, you can do:
179```javascript
180const api = new Urbit("", "", "landscape");
181```
183### `/session.js`
185Most functions of the `Urbit` class need to know the ship name or they will
186fail. This is given explicitly with the external authentication method [detailed
187below](#authentication), but not when using the `Urbit` constructor in a web app
188served directly from the ship. Rather than having the user manually enter it,
189Urbit ships serve a JS library at `/session.js` that contains the following:
191```javascript
192window.ship = "zod";
193```
195`"zod"` will be replaced with the actual name of the ship in question. You can
196include this file like:
198```
199<script src="/session.js"></script>
200```
202Then you need to set the `ship` field in the `Urbit` object. You would typically
203do it immediately after instantiating it:
205```javascript
206const api = new Urbit("");
207api.ship = window.ship;
208```
210### Channels
212With the exception of scries and threads, all communication with Eyre happens
213through its channel system.
215The `Urbit` object will generate a random channel ID that looks like
216`1646295453-e1bdfd`, and use a path of `/~/channel/1646295453-e1bdfd` for
217channel communications. Pokes will and subscription requests will be sent to
218that channel. Responses and subscription updates will be sent out to you on that
219channel too.
221Eyre sends out updates and responses on an [SSE] (Server Sent Event) stream for
222the channel. The `Urbit` object handles this internally with an `eventSource`
223object, but you won't deal with it directly. Eyre requires all events it sends
224out be acknowledged by the client, and will eventually close the channel if
225enough unacknowledged events accumulate. The `Urbit` object handles event
226acknowledgement automatically, so you don't need to worry about this.
228Eyre automatically creates a channel when a poke or subscription request is
229first sent to `/~/channel/[a new channel ID]`. If your web app is served
230external to the ship and you use the `authenticate` function [described
231below](#authentication), the function will automatically send a poke and open
232the channel. If your web app is served directly from the ship and you use
233`Urbit` class constructor, it won't open the channel right away. Instead, the
234channel will be opened whenever you first send a poke or subscription request.
236### Connection state
238`Urbit` class includes three optional callbacks that fire when the SSE
239connection state changes:
241| Callback | Description |
242| --------------- | ------------------------------------------------------------------------------------------------------------------------- |
243| `Urbit.onOpen` | Called when an SSE channel connection is successfully established. |
244| `Urbit.onRetry` | This is called whenever a reconnection attempt is made due to an interruption, for example if there are network problems. |
245| `Urbit.onError` | This is called when there is an unrecoverable error, for example after enough reconnection attemps have failed. |
247As mentioned in the previous section, typically a channel will be opened and an
248SSE connection established after you first poke the ship or make a subscription
249request. If successful, whatever function you provided to `onOpen` will be
250called. If at some point the connection is interrupted, a reconnection attempt will be made three times:
2521. Instantly.
2532. 750ms after the first.
2543. 3000ms after the second.
256Each attempt will call the function you provided to `onRetry`, if any. If all
257three reconnection attempts failed, or if a fatal error occurred, the function
258you provided `onError` will be called with an `Error` object containing an error
259message as its argument.
261How you use these (if you do at all) is up to you and depends on the nature of
262your app. If you want to try reconnecting when `onError` fires, note that Eyre
263will delete a channel if it's had no messages from the client in the last 12
264hours. The timeout is reset whenever it receives a message, including the acks
265that are automatically sent by the `Urbit` object in response to subscription
266updates.
268If you don't want to account for the possibility of the channel having been
269deleted in your app's logic, you can also just call the [`reset()`](#reset)
270function before you try reconnecting and consequently open a brand new channel.
272### Authentication
274**If your front-end is served directly from the Urbit ship, this step can be
275skipped.** If your web app is served externally to the ship, you must
276authenticate and obtain a session cookie before commencing communications with
277the ship.
279The `Urbit` class in `http-api` includes an `authenticate` function which does
280the following:
2821. Login to the user's ship with their `code` and obtain a session cookie.
2832. Generate a random channel ID for the connection.
2843. Poke the user's ship and print "opening airlock" in the dojo to initialize
285 the channel.
287The `authenticate` function takes four arguments in an object: `ship`, `url`,
288`code` and `verbose`:
290| Argument | Type | Description | Example |
291| --------- | --------- | -------------------------------------------------------------------------------------- | --------------------------------- |
292| `ship` | `string` | The ship ID (`@p`) without the leading `~`. | `"sampel-palnet"` or `"zod"` |
293| `url` | `string` | The base URL for the ship without the protocol. | `localhost:8080` or `example.com` |
294| `code` | `string` | The user's web login code. | `"lidlut-tabwed-pillex-ridrup"` |
295| `verbose` | `boolean` | Whether to log details to the console. This field is optional and defaults to `false`. | `true` |
297This function returns a promise that if successful, produces an `Urbit` object
298which can then be used for communications with the ship.
300**Note:** An Urbit ship will deny CORS requests from external URLs by default.
301In order to allow CORS for an external web app, the user will need to run the
302following in the dojo (replacing `https://example.com` with the address in
303question):
305```
306|cors-approve 'https://example.com'
307```
309#### Example
311Here's a simple example that will run `authenticate` for a fakezod when the
312_Connect_ button is pressed:
314```
315<html>
316 <head>
317 <script src="https://unpkg.com/@urbit/http-api"></script>
318 </head>
319 <body>
320 <button id="start" type="button" onClick="connect()" >Connect</button>
321 </body>
322 <script>
323 async function connect() {
324 window.api = await UrbitHttpApi.Urbit.authenticate({
325 ship: "zod",
326 url: "localhost:8080",
327 code: "lidlut-tabwed-pillex-ridrup",
328 verbose: true
329 });
330 document.body.innerHTML = "Connected!";
331 };
332 </script>
333</html>
334```
336`window.api` can then be used for further communications.
338## Functions
340The various functions you can use in the `Urbit` object are described below.
342### Poke
344For poking a ship, the `Urbit` class in `http-api` includes a `poke` function.
345The `poke` function takes six arguments in a object:
347| Argument | Type | Description | Example |
348| ----------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
349| `app` | `string` | The Gall agent to poke. | `"graph-store"` |
350| `mark` | `string` | The mark of the data to poke the agent with. | `"graph-update-3"` |
351| `json` | any JSON | The data to poke the agent with. | `{foo: "bar", baz: 42}` |
352| `ship` | `string` | The Urbit ID (`@p`) of the ship without the `~`. This may be ommitted if it's already been set for the whole `Urbit` object. | `"sampel-palnet"` |
353| `onSuccess` | A function. | This is called if the poke succeeded (the ship ack'd the poke). | `someFunction` |
354| `onError` | A function. | This is called if the poke failed (the ship nack'd the poke). | `anotherFunction` |
356#### Example
358This example lets you "hi" your ship with a given message, which will be printed
359in the terminal. It pokes the `hood` agent (the system agent) with a `helm-hi`
360mark. The `json` argument just contains the message in a string. If for some
361reason the `hood` agent rejects the poke, "Poke failed!" will be displayed on
362the page.
364```
365<html>
366 <head>
367 <script src="https://unpkg.com/@urbit/http-api"></script>
368 <script src="/session.js"></script>
369 </head>
370 <body>
371 <input id="msg" type="text" placeholder="Message for ship" />
372 <button id="submit" type="button" onClick="doPoke()" >Submit</button>
373 <p id="err"></p>
374 </body>
375 <script>
376 document.getElementById("msg")
377 .addEventListener("keyup", function(event) {
378 if (event.keyCode === 13) {
379 document.getElementById("submit").click();
380 }
381 })
382 const api = new UrbitHttpApi.Urbit("");
383 api.ship = window.ship;
384 function doPoke() {
385 const msg = document.getElementById("msg").value;
386 api.poke({
387 app: "hood",
388 mark: "helm-hi",
389 json: msg,
390 onSuccess: success,
391 onError: error
392 });
393 }
394 function success() {
395 document.getElementById("msg").value = "";
396 document.getElementById("err").innerHTML = "";
397 }
398 function error() {
399 document.getElementById("msg").value = "";
400 document.getElementById("err").innerHTML = "Poke failed!";
401 }
402 </script>
403</html>
404```
406### Subscribe and Unsubscribe {% #subscribe %}
408For subscribing to a particular path in an agent, the `Urbit` class in `http-api` includes a `subscribe` function.
409The `subscribe` function takes six arguments in a object:
411| Argument | Type | Description | Example |
412| -------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------- |
413| `app` | `string` | The Gall agent to which you'll subscribe. | `"graph-store"` |
414| `path` | `string` | The subscription path. | `"/updates"` |
415| `ship` | `string` | The Urbit ID (`@p`) of the ship without the `~`. This may be ommitted if it's already been set for the whole `Urbit` object. | `"sampel-palnet"` |
416| `err` | A function. | This is called if the subscription request fails. | `someFunction` |
417| `event` | A function. | This is the function to handle each update you receive for this subscription. The function's argument is the update's JSON data. | `anotherFunction` |
418| `quit` | A function. | This is called if you are kicked from the subscription. | `yetAnotherFunction` |
420The `subscribe` function returns a subscription ID, which is just a number. This
421ID can be used to unsubscribe down the line.
423If the subscription request is successful, you'll continue to receive updates
424until you either unsubscribe or are kicked by the agent. You may subscribe to
425multiple different agents and subscription paths by calling the `subscribe`
426function for each one.
428If you wish to unsubscribe from a particular subscription, the `Urbit` class in
429`http-api` includes an `unsubscribe` function. This function just takes a single
430argument: the subscription ID number of an existing subscription. Once
431unsubscribed, you'll stop receiving updates for the specified subscription.
433#### Example
435This example will, upon clicking the "Subscribe" button, subscribe to the
436`/updates` path of the `graph-store` agent, and print the raw JSON of each
437update it receives. You can test it by doing something like posting a message in
438a chat. You can also unsubscribe by clicking the "Unsubscribe" button.
440````
441<html>
442 <head>
443 <script src="https://unpkg.com/@urbit/http-api"></script>
444 <script src="/session.js"></script>
445 </head>
446 <body>
447 <button id="toggle" type="button" onClick="doSub()" >Subscribe</button>
448 <pre id="event">```
449 </body>
450 <script>
451 const api = new UrbitHttpApi.Urbit("");
452 api.ship = window.ship;
453 const toggle = document.getElementById("toggle");
454 const event = document.getElementById("event");
455 function doSub() {
456 window.id = api.subscribe({
457 app: "graph-store",
458 path: "/updates",
459 event: printEvent,
460 quit: doSub,
461 err: subFail
462 });
463 toggle.innerHTML = "Unsubscribe";
464 toggle.onclick = doUnsub;
465 event.innerHTML = "Awaiting event";
466 };
467 function doUnsub() {
468 api.unsubscribe(window.id);
469 toggle.innerHTML = "Subscribe";
470 toggle.onclick = doSub;
471 event.innerHTML = "Subscription closed";
472 };
473 function printEvent(update) {
474 event.innerHTML = JSON.stringify(update, null, 2);
475 };
476 function subFail() {
477 event.innerHTML = "Subscription failed!";
478 };
479 </script>
480</html>
481````
483### Subscribe Once
485The `subscribeOnce` function in the `Urbit` class of `http-api` is a variation
486on the ordinary [`subscribe`](#subscribe) function. Rather than keeping the
487subscription going and receiving an arbitrary number of updates, instead it
488waits to receive a single update and then closes the subscription. This is
489useful if, for example, you send a poke and just want a response to that one
490poke.
492The `subscribeOnce` function also takes an optional `timeout` argument, which
493specifies the number of milliseconds to wait for an update before closing the
494subscription. If omitted, `subscribeOnce` will wait indefinitely.
496`subscribeOnce` takes three arguments (these can't be in an object like most
497other `Urbit` functions):
499| Argument | Type | Description | Example |
500| --------- | -------- | ------------------------------------------------------------------------------------------------------------------------ | --------------- |
501| `app` | `string` | The Gall agent to which you'll subscribe. | `"graph-store"` |
502| `path` | `string` | The subscription path. | `"/updates"` |
503| `timeout` | `number` | The number of milliseconds to wait for an update before closing the subscription. If omitted, it will wait indefinitely. | `5000` |
505`subscribeOnce` returns a Promise. If successful, the Promise produces the JSON
506data of the update it received. If it failed due to either timing out or getting
507kicked from the subscription, it will return an error message of either
508`"timeout"` or `"quit"`.
510#### Example
512Here's a variation on the ordinary [subscribe](#subscribe) function. Rather than
513printing every `graph-store` update it receives, it will instead just print the
514first one it receives and close the subscription. Additionally, it sets a five
515second timeout, and prints an error message if it times out.
517````
518<html>
519 <head>
520 <script src="https://unpkg.com/@urbit/http-api"></script>
521 <script src="/session.js"></script>
522 </head>
523 <body>
524 <button type="button" onClick="doSub()" >Subscribe Once</button>
525 <pre id="event">```
526 </body>
527 <script>
528 const api = new UrbitHttpApi.Urbit("");
529 api.ship = window.ship;
530 const event = document.getElementById("event");
531 function doSub() {
532 event.innerHTML = "";
533 api.subscribeOnce("graph-store","/updates",5000)
534 .then(printEvent, noEvent);
535 };
536 function printEvent(update) {
537 event.innerHTML = JSON.stringify(update, null, 2);
538 };
539 function noEvent(error) {
540 event.innerHTML = error;
541 };
542 </script>
543</html>
544````
546### Scries
548To scry agents on the ship, the `Urbit` class in `http-api` includes a `scry` function.
549The `scry` function takes two arguments in a object:
551| Argument | Type | Description | Example |
552| -------- | -------- | ---------------------------------- | --------------- |
553| `app` | `string` | The agent to scry. | `"graph-store"` |
554| `path` | `string` | The path to scry, sans the `care`. | `"/keys"` |
556The `scry` function returns a promise that, if successful, contains the
557requested data as JSON. If the scry failed, for example due to a non-existent
558scry endpoint, connection problem, or mark conversion failure, the promise will
559fail.
561#### Example
563Upon pressing the "Scry Graphs" button, this example will scry the `graph-store`
564agent's `/keys` endpoint for the list of graphs, and print the resulting JSON
565data.
567````
568<html>
569 <head>
570 <script src="https://unpkg.com/@urbit/http-api"></script>
571 <script src="/session.js"></script>
572 </head>
573 <body>
574 <button id="scry" type="button" onClick="doScry()" >Scry Graphs</button>
575 <pre id="result">```
576 </body>
577 <script>
578 const api = new UrbitHttpApi.Urbit("");
579 api.ship = window.ship;
580 async function doScry() {
581 var groups = await api.scry({app: "graph-store", path: "/keys"});
582 document.getElementById("result")
583 .innerHTML = JSON.stringify(groups, null, 2);
584 }
585 </script>
586</html>
587````
589### Thread
591To run a thread, the `Urbit` class in `http-api` includes a `thread` function.
592The `thread` function takes five arguments in an object:
594| Argument | Type | Description | Example |
595| ------------ | -------- | ------------------------------------------------------------------------------------------------------------- | ----------------------- |
596| `inputMark` | `string` | The mark to convert your JSON data to before giving it to the thread as its argument. | `"graph-view-action"` |
597| `outputMark` | `string` | The result of the thread should be converted to this mark before being converted to JSON and returned to you. | `"tang"` |
598| `threadName` | `string` | The name of the thread to run. | `"graph-eval"` |
599| `body` | any JSON | The data to give to the thread as its argument. | `{foo: "bar", baz: 42}` |
600| `desk` | `string` | The desk in which the thread resides. This may be ommitted if previously set for the whole `Urbit` object. | `"landscape"` |
602The `thread` function will produce a promise that, if successful, contains the
603JSON result of the thread. If the thread failed, a connection error occurred, or
604mark conversion failed, the promise will fail.
606#### Example
608This example takes a hoon expression (such as `(add 1 1)`), evalutes it with the
609`graph-eval` thread in the `landscape` desk, and prints the result.
611````
612<html>
613 <head>
614 <script src="https://unpkg.com/@urbit/http-api"></script>
615 <script src="/session.js"></script>
616 </head>
617 <body>
618 <input id="hoon" type="text" placeholder="Hoon to evaluate" />
619 <button id="submit" type="button" onClick="runThread()" >Submit</button>
620 <pre id="expr">```
621 <pre id="result">```
622 </body>
623 <script>
624 document.getElementById("hoon")
625 .addEventListener("keyup", function(event) {
626 if (event.keyCode === 13) {
627 document.getElementById("submit").click();
628 }
629 });
630 const hoon = document.getElementById("hoon");
631 const expr = document.getElementById("expr");
632 const result = document.getElementById("result");
633 const desk = "landscape";
634 const api = new UrbitHttpApi.Urbit("", "", desk);
635 api.ship = window.ship;
636 async function runThread() {
637 const threadResult = await api.thread({
638 inputMark: "graph-view-action",
639 outputMark: "tang",
640 threadName: "graph-eval",
641 body: {eval: hoon.value}
642 });
643 result.innerHTML = threadResult[0].join("\n");
644 expr.innerHTML = hoon.value;
645 hoon.value = "";
646 };
647 </script>
648</html>
649````
651### Delete Channel
653Rather than just closing individual subscriptions, the entire channel can be
654closed with the `delete` function in the `Urbit` class of `http-api`. The
655function takes no arguments, and can be called like `api.cancel()` (replacing
656`api` with whatever you've named the `Urbit` object you previously instantiated).
657When a channel is closed, all subscriptions will be cancelled and all pending
658updates will be discarded.
660### Reset
662An existing instance of `http-api`'s `Urbit` class can be reset with its
663`reset` function. This function takes no arguments, and can be called like
664`api.reset()` (replacing `api` with whatever you've named the `Urbit` object you
665previously instantiated). When called, the channel will be closed and all
666subscriptions cancelled. Additionally, all outstanding outbound pokes will be
667discarded and a fresh channel ID will be generated.
669## Further reading
671- [Eyre External API Reference][eyre-ext-ref] - Lower-level documentation of
672 Eyre's external API.
673- [Eyre Guide][eyre-guide] - Lower-level examples of using Eyre's external API
674 with `curl`.
675- [`http-api` on Github][http-api-src] - The source code for `@urbit/http-api`.
677## Examples
679Here are some examples which make use of `@urbit/http-api` that might be useful
680as a reference:
682- [Bitcoin Wallet](https://github.com/urbit/urbit/tree/master/pkg/btc-wallet)
683- [Web Terminal](https://github.com/urbit/urbit/tree/master/pkg/interface/webterm)
684- [UrChatFM](https://github.com/urbit/urbit-webrtc/tree/master/urchatfm)
686[eyre-ext-ref]: /docs/arvo/eyre/external-api-ref
687[eyre-guide]: /docs/arvo/eyre/guide
688[http-api-src]: https://github.com/urbit/urbit/tree/master/pkg/npm/http-api
689[eyre]: /docs/glossary/eyre
690[vane]: /docs/glossary/vane
691[arvo]: /docs/glossary/arvo
692[hoon]: /docs/glossary/hoon
693[gall]: /docs/glossary/gall
694[clay]: /docs/glossary/clay
695[desk]: /docs/glossary/desk
696[mark]: /docs/glossary/mark
697[sse]: https://html.spec.whatwg.org/#server-sent-events
698[@urbit/http-api]: https://github.com/urbit/urbit/tree/master/pkg/npm/http-api