authenticated HTTP relay

You write the logic.
Relay it to us.
We execute the rest.

Backfire is one endpoint for outbound HTTP. Your app sends a URL, a method and a body; we validate the key, guard the target, make the call, and hand back one predictable JSON envelope. Retries, branching and mapping stay in your code, where they belong.

no card · no subscription · balance never expires

1 trip = 1 complete roundtrip (1 uptrip + 1 downtrip)

600 forwards a minute per key · 10 s cooldown past that, never charged

no trace log · trip data purged the moment it completes

The whole product1 hop
youyour app
Backfire logobackfire
upstreamtarget API

bearer

backfire_••••

body

JSON envelope

trail

full log

The problem

Outbound HTTP is where careful applications quietly get unsafe.

Every application eventually calls someone else's API. In a serverless function, an agent tool call or a webhook handler, that URL is frequently supplied by whoever is on the other end — which makes an ordinary fetch() an untrusted network primitive.

Writing the safe version yourself means re-implementing the same four things in every project: target validation that actually resists SSRF, timeouts that are enforced rather than hoped for, size limits, and a record of what was called. Most teams ship the call and skip the rest.

Backfire exists so the safe version is shorter than the unsafe one: one authenticated POST, one predictable envelope, one log line per attempt.

Illustration: a request arcing toward a guarded target

SSRF is one fetch() away

Timeouts nobody enforces

SSRF is one fetch() away

The URL often comes from a user, a model or a webhook. Resolve it carelessly once and the request lands on a metadata service or an internal admin port.

Timeouts nobody enforces

A slow upstream holds your invocation open, your retry pile doubles, and the failure looks like your bug in production.

Bodies with no ceiling

Without a size bound, one oversized response turns into a memory spike and a page for whoever is on call.

No record of what happened

After an incident, "we called their API" is not an answer. You need the target, the status, the timing and the byte counts.

How it works

Three steps from a key to a guarded call.

Read the quickstart
  1. 01

    Mint a key

    Create an account, open the console and mint a backfire_…_api key. The full value is shown exactly once; only a hash and a display prefix are kept.

  2. 02

    Send one JSON POST

    Point your app at the forward endpoint with your key as a bearer token. Name the target, the method, any headers and the body you want forwarded.

  3. 03

    Read the envelope

    You get ok, the upstream status and headers, the parsed body, the metering figures and your remaining balance — in one predictable shape.

the entire integration
POST /functions/forward
Authorization: Bearer backfire_…_api
Content-Type: application/json

{
  "url": "https://api.github.com/zen",
  "method": "GET",
  "timeout_ms": 8000
}

Nothing else to install. No SDK, no agent to deploy and no egress sidecar. If your runtime can send an HTTPS POST, it can use the relay — which is also why the same key works from Node, Python, Go, a shell script or an edge worker.

The contract

JSON in. JSON envelope out.

Upstream JSON comes back parsed. Anything else comes back as text, or base64 with an encoding flag. Usage is always in the envelope — no separate metering API to poll.

request
POST /functions/forward
Authorization: Bearer backfire_4c9aE7Qp…_api

{
  "url": "https://api.stripe.com/v1/charges",
  "method": "POST",
  "headers": { "Idempotency-Key": "ord_8123" },
  "body": { "amount": 4200, "currency": "usd" },
  "timeout_ms": 12000
}
response
{
  "ok": true,
  "upstream": { "status": 200, "status_text": "OK" },
  "body": { "encoding": "json", "json": { "id": "ch_3Pq…" } },
  "meta": {
    "bytes_in": 96,
    "bytes_out": 1482,
    "trips_charged": 1,
    "duration_ms": 214,
    "target_host": "api.stripe.com"
  },
  "quota": { "remaining": 4999, "unit": "trips" }
}

Code

Same three fields in every language.

There is no Backfire SDK to learn. The request is a JSON object with a target, a method and an optional timeout — so the examples below differ only in syntax.

  • body accepts an object (sent as JSON) or a string (sent as text).
  • body_base64 sends binary payloads and takes precedence over body.
  • Language-specific guides: Node, Python, Go, PHP, Ruby, curl.
curl -sS https://backfireapi.base44.app/functions/forward \
  -H "Authorization: Bearer $BACKFIRE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://api.github.com/zen",
    "method": "GET",
    "timeout_ms": 8000
  }'

Use cases

The calls worth putting behind a guard.

Each one below has its own page with the request shape, the failure modes and the errors you will actually see.

Agent tool calls

AI agents & tool calls

An agent that can call arbitrary URLs is an agent that can be talked into calling your metadata service.

read the page

User-supplied URLs

User-supplied URLs

The moment your product fetches a URL a user typed, an attacker controls your outbound request.

read the page

Serverless & edge

Serverless egress

Serverless functions make outbound calls in a dozen places, each with its own timeout, its own error handling and no shared record.

read the page

Webhook delivery

Webhook delivery

Outbound webhook delivery is outbound HTTP with extra ways to fail: customer endpoints go down, DNS breaks, and every retry needs to be explicable.

read the page

Pipelines & backfills

Data pipelines

Pipelines are bursts: quiet for a week, then a hundred thousand calls during a backfill.

read the page

Customer integrations

Multi-tenant integrations

When every customer brings their own API endpoint, your outbound code becomes a multi-tenant system with a single point of failure.

read the page

MCP & remote tools

MCP servers

An MCP server is a request executor for clients you do not control.

read the page

Ingestion & retrieval

RAG ingestion

Retrieval pipelines fetch whatever a source document happens to link to.

read the page

Health checks

Uptime monitoring

A monitor is only useful if its failure modes are boring.

read the page

Scheduled crawls

Scheduled crawls

A crawl is a loop you leave alone.

read the page

Asset pipelines

Asset pipelines

Asset pipelines fetch URLs chosen by users and partners: an avatar link, a storefront image, a scanned receipt.

read the page

Testing & CI

Integration tests

Tests that reach real third-party APIs fail for reasons unrelated to your code.

read the page

Compare

What you stop rebuilding.

Backfire is not a replacement for your application logic, and it is not an API gateway. It replaces the safety, credential and audit layer around outbound calls — the part that has to be correct before anything else in your integration matters.

SSRF guard on every target

fetch()
You build it
proxy fn
You build it
Backfire
Included

Fails closed on ambiguous DNS

fetch()
Depends
proxy fn
Depends
Backfire
Included

Redirects never auto-followed

fetch()
Configure it
proxy fn
Configure it
Backfire
Default

Bearer keys hashed at rest

fetch()
You build it
proxy fn
You build it
Backfire
Included

Revocation on the next request

fetch()
—
proxy fn
Redeploy
Backfire
One click

Per-call audit log with detail

fetch()
—
proxy fn
You build it
Backfire
Included

Usage metering in the response

fetch()
—
proxy fn
—
Backfire
Included

Bounded payload and timeout

fetch()
You set it
proxy fn
You set it
Backfire
Enforced

Code you own and maintain

fetch()
~10 lines
proxy fn
An endpoint + IAM
Backfire
One POST

"You build it" means the capability is achievable in your own code — it is simply work you own, test and keep current.

Security & trust

The guard rails, stated plainly.

Acceptable use policy
Illustration: a shield deflecting requests before they reach a server

Target safety, checked before the call

  • Private, loopback, link-local, multicast and cloud-metadata ranges blocked on IPv4 and IPv6.
  • DNS failures and ambiguous answers fail closed rather than guessing.
  • Redirects are never followed; a 3xx comes back to you with its location header.

Credentials that are not stored

  • Keys are shown once and kept as a SHA-256 hash plus a display prefix.
  • Revocation applies to the very next request — no cache window.
  • Only the forward endpoint accepts a relay key, so a leaked key cannot read your balance.

Transport that is always encrypted

  • Every interaction — sign-in, key management, relays, log reads — travels over HTTPS.
  • A request made over plain HTTP is redirected to HTTPS before it is served.
  • Keys are compared as hashes, never stored or echoed back in plaintext.

Limits that are enforced, not advisory

  • Timeouts capped at 30 seconds and clamped to your value.
  • Per-call size allowances keep any single request bounded.
  • 600 forwards a minute per key — bursts past that cool down for 10 seconds and are never charged.
  • A completed trip leaves its counters and nothing else — target, headers, body and response go the moment it ends.

What Backfire is not: it is not an anonymity layer, not a WAF, and not a replacement for your own application-level allow-lists. Every call is attributable to the key that made it. A refused attempt keeps its full detail — target, redacted headers, reason — so you can audit it; a completed trip keeps its counters and nothing else. None of this has been independently audited, and there is no SLA: these are the controls as implemented, not a certification.

Throughput, stated up front

600 forwards a minute. Then a ten-second breath.

Backfire is not a dumb socket. Every forward is authorised, checked and metered against a ledger on both legs: your key and the target on the way out — the uptrip — and the response, byte counts and the balance that follows on the way back, the downtrip.

That ledger is the whole product. It is why revoking a key stops the very next request, why a loopback or metadata address is refused before a connection ever opens, and why a balance always settles to the exact trip. It runs in its own database, so metering never queues behind anything else the app is doing — the ceiling below is capacity, not a shared budget.

So each key gets a burst budget of 600 forwards per rolling minute, and each account 1,200, spendable all at once. Cross it and the call comes back immediately with 429 rate_limited and a retry_after_seconds of 10 — one ten-second cooldown, then try again. Nothing is sent upstream, nothing is metered, and the minute keeps draining while you wait.

And nothing about the trip outlives it. There is no trace log and no retained copy: the target, the headers, the body you sent and the answer that came back are wiped the instant the roundtrip completes, so the only thing that survives is its counters — host, status, byte counts and trips.

600forwards per key

in a rolling minute — the whole budget can go at once

10 scooldown

handed back on every refusal as retry_after_seconds

0 tripscharged while throttled

no upstream connection is opened at all

the refusal

{ "ok": false,
  "error": { "code": "rate_limited" },
  "retry_after_seconds": 10 }

Need to move faster than 600 a minute? Three patterns keep real traffic comfortably inside the budget:

Queue, do not fan out

One worker per key with a short pause between calls stays inside the burst budget for as long as you like — 600 a minute sustained, indefinitely.

Batch upstream

Most APIs accept many items per request. One forward carrying ten records is one trip and one ledger entry, not ten of each.

Stream it instead

Anything long-lived belongs on the socket bridge: one ticket holds the connection open and relays frames, instead of one forward per message.

Why it exists

Controlled execution, not a workflow builder.

The hard parts of running an outbound relay are safety, metering and revocation. That is the whole feature list.

SSRF-guarded by default

Every target is resolved and checked against private, loopback, link-local and cloud-metadata ranges on IPv4 and IPv6. Ambiguous DNS fails closed.

Keys hashed at rest

backfire_…_api keys are shown once, stored as a one-way hash plus a display prefix. Revocation takes effect on the very next request.

A balance, not invoices

Top up once with a pack that never expires. No subscription, no seats, no renewal date — and the console shows what every call drew.

Redirects off

We do not chase hops for you. Hop-by-hop headers are stripped, CR/LF is rejected, and unsafe upstream behavior stays off unless you ask for it.

Capped everywhere

Bounded payloads, a 30 second ceiling, per-key and per-account rate limits. Predictable failure beats an open relay.

Audit trail, in the open

Every call, deny and key change is logged line by line with its full detail — readable in the dashboard and exportable as JSON, CSV or Markdown.

Backfire is

  • An authenticated HTTP relay for developers
  • One endpoint, bearer keys, a balance you top up
  • A safety layer in front of your outbound calls
  • Infrastructure you can revoke in one click

Backfire is not

  • An anonymous open proxy
  • A browser extension or consumer link tool
  • A full enterprise API gateway
  • A place where you upload proxy code for us to run

Billing

A balance,
not a bill.

Your account carries a balance of trips. One trip is one complete roundtrip: your request goes up, the answer comes back down, and the trip is spent once. Everything you have left stays yours.

alwaysno subscription · no expiry · no seats

What one trip is

1 trip=1 complete roundtrip= 1 uptrip+ 1 downtrip

One uptrip carries your request to the target, one downtrip carries its answer back. A call that reaches the network draws one trip; a request rejected before it does draws none.

Illustration: a ledger of trips beside a balance scale

Rejected requests cost nothing

A bad key, a blocked target, a rate limit or an oversized body never touches your balance.

Completed calls are charged once

One trip is one complete roundtrip — an uptrip out and a downtrip back. The exact draw is written into the envelope and the log.

The balance never expires

Top up once and spend it whenever, across every key on the account. No renewal, no seats, nothing to cancel.

FAQ

Questions developers actually ask.

If something is not answered here, the reference covers the request shape, the envelope and every error code in detail.

Read the documentation →

Go deeper

Pick the page that matches your problem.

Start with 5,000 free trips

Point your app at one endpoint.

Mint a key, run a forward, read the envelope. Most developers get their first 200 back in under five minutes.