Agents without a browser
This guide connects an agent that cannot open a web page: a sandboxed cloud agent, a job on a build server, or anything headless. The agent prints a short code, and you approve it on your Mac. The guide is written for whoever builds or instructs the agent; the part the user does is one click.
This is the OAuth device flow. The result is the same durable connector as in Connect ChatGPT, with the same access.
How it works#
- The agent registers with the relay and asks for a code.
- The relay gives the agent a user code such as
BDFG-HJKM, and shows a banner on your Mac with the same code. - The agent tells you the code. You compare it with the banner and press Approve.
- The agent, which has been asking the relay whether you approved, receives its tokens.
The code exists so that you can tell that the request on your Mac is the one your agent just made.
Before you start#
-
The relay is set up and Settings > Cloud shows Online. See Cloud relay.
-
The agent can make HTTPS requests and knows the relay's address. The examples use these variables:
RELAY="https://herald-relay.example.workers.dev" UA="User-Agent: Herald-Agent/1.0"Send a
User-Agentof your own on every request. Cloudflare rejects some library defaults. See Use a custom domain.
Steps for the agent#
-
Register once and keep the
client_idfrom the reply.curl -s "$RELAY/register" -H "$UA" -H "Content-Type: application/json" \ -d '{"client_name": "My cloud agent", "grant_types": ["urn:ietf:params:oauth:grant-type:device_code"]}'The
client_nameis the name the user will see. -
Ask for a device code.
curl -s "$RELAY/device_authorization" -H "$UA" -d client_id="$CLIENT_ID" -d scope=notifyThe reply holds the codes and says which Mac will be asked:
{ "device_code": "hrv_...", "user_code": "BDFG-HJKM", "verification_uri": "https://herald-relay.example.workers.dev/activate", "verification_uri_complete": "https://herald-relay.example.workers.dev/activate?user_code=BDFG-HJKM", "expires_in": 600, "interval": 5, "device_name": "Studio Mac", "device_count": 1, "device_index": 1, "device_online": true } -
Tell the user the code, in words like: "Approve my Herald request on your Mac. The code is BDFG-HJKM."
On the Mac, a banner appears the moment step 2 returns: Approve My cloud agent to send you notifications? Code BDFG-HJKM, with Approve and Deny.
-
Ask for the tokens every
intervalseconds until the answer changes.curl -s "$RELAY/token" -H "$UA" \ -d grant_type=urn:ietf:params:oauth:grant-type:device_code \ -d client_id="$CLIENT_ID" -d device_code="$DEVICE_CODE"While the user has not decided, the reply is a
400with anerror:errorMeaning What to do authorization_pending The user has not answered yet. Keep asking. slow_downYou asked more often than interval.Add 5 seconds to your interval. access_deniedThe user pressed Deny. Stop. expired_token10 minutes passed. Start again at step 2. When the user approves, the reply is the tokens:
{ "access_token": "hra_...", "refresh_token": "hrr_...", "token_type": "Bearer", "expires_in": 315360000, "scope": "notify" } -
Use the access token as a bearer token on the relay.
curl -s -X POST "$RELAY/v1/notify" -H "$UA" \ -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" \ -d '{"title": "Connected", "body": "This agent can now notify you."}'Keep the token. It does not expire and is never replaced, so there is nothing to refresh. It works until the user revokes the connector in Herald.
What the user does#
The user compares the code the agent printed with the code in the banner and presses Approve if they match.
If the banner was missed, the request is in Settings > Cloud > Connector approvals, with the agent's code in large type and Approve and Deny beside it.
The relay also serves a page at /activate for a person who prefers a browser. It asks for the agent's code
and then for the 6-digit approval code that Herald shows. The agent's code alone is not enough there, because
the agent knows it.
Rules worth knowing#
- A request that nobody answers expires after 10 minutes. At most 3 wait at once, and 12 an hour.
- Asking for a device code again replaces your open request. The old
device_codestops working and the banner is updated with the new code. - With several Macs paired, the relay asks the Mac that is connected right now, or else the one seen most
recently. Add
-d device=2in step 2 to choose the second Mac in pairing order. Tell the user which Mac will show the banner, usingdevice_namefrom the reply. - A repeated poll after approval returns the tokens again, so a lost response costs nothing.
Related#
- Relay API: device flow: every parameter of these requests.
- Reply events: be told when the user replies.
GET /v1/relay/instructions: get this sequence as text, with the relay's address filled in.