HyperScript
Docs

← All of the Docs Data and the outside

Web requests

effectsend get request to <url>since 1.4.0

Fetch a URL; the value is the response body as text

set {_response} to send get request to "https://api.mojang.com/users/profiles/minecraft/" + urlencoded arg.1
Try it
effectsend post request to <url> with body <text>since 1.4.0

Send data. A body starting with { or [ is sent as JSON, anything else as plain text

send post request to "https://api.example.com/v1/analytics" with body "{\"event\":\"command_used\",\"player\":\"%player.name%\"}"
Try it
expressionheaders <text>, <text>, ...since 1.4.0

Optional, after the body: lines such as "Authorization: Bearer abc"

async:
    send post request to "https://api.example.com/v1/events" with body "{\"type\":\"join\"}" headers "Authorization: Bearer abc123"
Try it
expression{_body}since 1.7.17

What was sent, as text; json value "<path>" of {_body} reads from it if it is JSON

on web request "/announce":
    broadcast "&e[Web] &f%{_body}%"
Try it
expression{_method}since 1.7.17

GET, POST, ...

on web request "/ping":
    log "%{_method}% from %{_address}%"
Try it
expression{_path}since 1.7.17

The address asked for, e.g. /vote

on web request:
    log "Something asked for %{_path}%"
Try it
expression{_query}since 1.7.17

What came after ? in the address, without the token

on web request "/give":
    log "Asked with: %{_query}%"
Try it
expression{_address}since 1.7.17

Where the request came from

on web request "/vote":
    log "A vote arrived from %{_address}%"
Try it
Guide and examples

Scripts can call web APIs. A request waits for the server on the other end, so one whose answer you use has to be inside an async: block; HyperScript refuses to run it on the server thread rather than freeze the game.

command /lookup <player>:
    trigger:
        send "&eFetching data from the API..."
        async:
            set {_response} to send get request to "https://api.mojang.com/users/profiles/minecraft/" + urlencoded arg.1
            set {_uuid} to json value "id" of {_response}
            sync:
                if {_uuid} is set:
                    send "&aFound UUID for %arg.1%: &f%{_uuid}%"
                else:
                    send "&cPlayer profile not found."

command /postanalytics:
    trigger:
        send post request to "https://api.example.com/v1/analytics" with body "{\"event\":\"command_used\",\"player\":\"%player.name%\"}"
SyntaxMeaning
send get request to <url>Fetch a URL; the value is the response body as text
send post request to <url> with body <text>Send data. A body starting with { or [ is sent as JSON, anything else as plain text
headers <text>, <text>, ...Optional, after the body: lines such as "Authorization: Bearer abc"
  • Used as a value (set {_r} to send get request ...), a request must be in async:. Used as a statement, where nothing reads the answer, it may appear anywhere and is sent in the background.
  • The value is unset if the request could not be made (no connection, unknown host, timeout); the reason is logged. An error response such as 404 still gives you its body.
  • Most APIs answer in JSON. json value "<path>" of <text> picks a value out of it, as the example does; see below.
  • Always put player input through urlencoded before adding it to a URL, as above. Otherwise a player can type characters that change which address or parameters the request uses.
  • Requests can only go to public internet addresses. Addresses on the server's own machine or local network (localhost, 192.168.x.x, 10.x.x.x and similar) are refused, so a URL built from player input cannot be used to reach private services. Set web.allow-private-addresses: true in config.yml if your scripts need to call something on your own network.
  • Redirects are not followed, timeouts are 5 seconds to connect and 10 to answer, and answers are cut off at 1 MB. web.enabled: false turns web requests off.

Reading JSON. json value takes a path and JSON text and gives the value found there.

set {_json} to "{\"name\":\"Steve\",\"stats\":{\"kills\":12},\"friends\":[\"Alex\",\"Bob\"]}"
log json value "name" of {_json}
log json value "stats.kills" of {_json}
log json value "friends.2" of {_json}
log size of (json value "friends" of {_json})

That logs Steve, 12, Bob and 2.

Path stepPicks
a name, such as statsThat field of an object. Capitals are ignored if there is no exact match
a number, such as 2That element of a list, counting from 1 like the rest of HyperScript
steps joined by dots, stats.killsOne inside the other
  • Numbers, text and true/false come out as ordinary values, and a JSON list comes out as a list.
  • A JSON object comes out as JSON text, which you can read further with another json value.
  • If the text is not JSON, or there is nothing at that path, the result is unset, so if {_value} is set: tells you whether it worked.

Being called from outside. HyperScript can also listen: a voting site, a web shop or a Discord bot calls an address of the server, and a script runs.

on web request "/vote":
    set {_name} to json value "username" of {_body}
    broadcast "&a%{_name}% voted for the server!"

It is off until web.server.enabled is true in config.yml, and it needs web.server.token set to a secret of at least 12 characters. The address is then http://<your server>:8765/vote (the port is web.server.port), and every request has to carry the token: as a header Authorization: Bearer <token> or X-Token: <token>, or as ?token=<token> in the address.

In the blockWhat it holds
{_body}What was sent, as text; json value "<path>" of {_body} reads from it if it is JSON
{_method}GET, POST, ...
{_path}The address asked for, e.g. /vote
{_query}What came after ? in the address, without the token
{_address}Where the request came from
  • on web request: without an address hears every request.
  • The caller is answered ok when a block ran and 404 when none listens at that address; a script cannot choose the answer. Without the right token the answer is 401 and no script runs.
  • A request may carry up to 64 KB, and no more than twenty are taken in a second.
  • The block runs as the console. Open the port in your firewall or with your host if the caller is not on the same machine, and prefer bind: "127.0.0.1" when it is.