Your agent can call any API. It can't be called back. So I built the other half.
Your agent can call any API. It can't be called back. So I built the other half.
An AI agent calling an API is a solved problem — that's the easy half of the agent web. The hard half is the reverse: being called back. The moment an agent needs to receive something — an OAuth redirect after a login, a "your deploy finished" webhook, a Stripe payment confirmation, a human clicking approve — it hits a wall. A process running inside a terminal, a CI job, or a Claude Code session has no public URL. There is nowhere for the callback to land.
So I built relais: the missing inbound address for a process that has none. It gives your agent a URL that catches any HTTP request, and — the part I like most — a long-poll the agent can curl and block on until the thing arrives. No poll loop, no infrastructure. Three curls.
Watch it catch a real GitHub webhook
This is the exact flow every agent integration eventually hits, run end to end against production. First, make an inbox:
I=$(curl -s -X POST https://relais.intrane.fr/v1/inboxes -d '{"label":"gh"}')
CATCH=$(echo "$I" | jq -r .catch_url); TOK=$(echo "$I" | jq -r .token)Now the agent blocks on the inbox — this call parks the connection open until something lands, instead of hammering a poll loop:
curl -s "https://relais.intrane.fr/v1/wait?timeout_ms=45000" \
-H "Authorization: Bearer $TOK" &Then point a real GitHub webhook at the catch URL. GitHub fires a ping event the instant you create one:
gh api repos/<you>/<repo>/hooks -X POST -f name=web -F active=true \
-f 'events[]=push' -f "config[url]=$CATCH" -f 'config[content_type]=json'The blocked /v1/wait returns the moment the ping arrives — with the real payload:
{"waiting":false,"message":{"method":"POST",
"headers":{"x-github-event":"ping"},
"body":"{\"zen\":\"Half measures are as bad as nothing at all.\",\"hook_id\":653798437,…}"}}A real third party (GitHub) delivered a real webhook to an agent that otherwise had no way to receive one — caught, blocked-on, and read. Swap GitHub for an OAuth provider, a CI pipeline, or Stripe and it's the same three curls. Secrets like Authorization and Cookie are never stored; the body is capped.
Free is ephemeral, persistent is paid
A free inbox expires in an hour and holds 100 messages — perfect for a one-shot OAuth dance. But if you need a stable URL to register with Stripe or GitHub or an OAuth app, you want it to stick around. So a persistent inbox (30-day TTL, thousands of messages) is bought with a peage wallet — one header, no subscription:
curl -s -X POST https://relais.intrane.fr/v1/inboxes \
-H 'X-Peage-Wallet: pw_…' -d '{"label":"stripe prod"}'This is the pattern I keep coming back to. péage is the pay half of the agent web — a fiat rail where an agent holds a prepaid wallet its human funds once, and any API charges it per call. relais is the receive half. An agent funds a wallet on péage, then spends it on relais (or grepapi, or hart). Two French infrastructure words for the two things an autonomous process can't do for itself: pay, and be reached.
Written for agents, not people
There's no dashboard. The docs are at relais.intrane.fr/llms.txt, written for the machine that will actually use them. The whole service is a single ~110 KB static binary in machin, my machine-first language — no Node, no runtime, no container. It runs, it catches, it waits.
If your agents need to be reachable — and eventually they all do — point a webhook at relais.intrane.fr and block on it. Source and the full walkthrough: github.com/javimosch/relais. I answer email: javi@intrane.fr.