# Proxy setup

> Let a browser or mobile app use Refabric through your own server, without ever holding your API key.

Keys are for servers: the API refuses any request that carries an `Origin` header
with `403 api_key_not_allowed_here`. A web or mobile app therefore talks to **your** server, and your
server calls Refabric with the key. The examples below are complete enough to adapt.

## How it works

```
your app  ──(your login)──▶  your proxy  ──(Authorization: Key …)──▶  https://api.refabric.com
```

1. Your app calls your proxy with **your** user's credentials (a sign-in cookie, a token).
2. The proxy checks that user, checks the request against what you allow, and forwards it to
   Refabric with the API key added.
3. The proxy returns Refabric's answer to the app.

Two rules make the forward work:

- **Add the key on the server** — `Authorization: Key $REFABRIC_API_KEY` from the environment.
- **Do not forward the browser's `Origin` header.** A keyed request that reaches Refabric with it
  is refused. Send only the headers you choose: `Authorization`, `Content-Type`, and
  `Idempotency-Key` or `Prefer` when you use them.

## Express

```javascript
import express from "express";

const API = "https://api.refabric.com/v1";
const ALLOWED_TASKS = new Set(["image.change_background", "image.upscale"]);
const app = express();
app.use(express.json());

const forward = async (res, path, init = {}) => {
  const r = await fetch(`${API}${path}`, {
    ...init,
    headers: { Authorization: `Key ${process.env.REFABRIC_API_KEY}`, "Content-Type": "application/json", ...init.headers },
  });
  res.status(r.status).type("application/json").send(await r.text());
};

app.post("/api/refabric/tasks/:name", requireUser, async (req, res) => {
  if (!ALLOWED_TASKS.has(req.params.name)) return res.status(403).json({ error: "task not allowed" });
  await forward(res, `/tasks/${req.params.name}`, {
    method: "POST",
    body: JSON.stringify(req.body),
    headers: { "Idempotency-Key": req.get("Idempotency-Key") ?? crypto.randomUUID() },
  });
});

app.get("/api/refabric/jobs/:id", requireUser, (req, res) => forward(res, `/jobs/${encodeURIComponent(req.params.id)}`));
app.get("/api/refabric/jobs/:id/result", requireUser, (req, res) => forward(res, `/jobs/${encodeURIComponent(req.params.id)}/result`));

app.listen(3001);
```

`requireUser` is your own authentication middleware.

## Next.js (route handler)

```javascript
// app/api/refabric/tasks/[name]/route.js
const API = "https://api.refabric.com/v1";
const ALLOWED_TASKS = new Set(["image.change_background", "image.upscale"]);

export async function POST(request, { params }) {
  const user = await currentUser(request); // your own sign-in check
  if (!user) return Response.json({ error: "sign in" }, { status: 401 });
  if (!ALLOWED_TASKS.has(params.name)) return Response.json({ error: "task not allowed" }, { status: 403 });

  const r = await fetch(`${API}/tasks/${params.name}`, {
    method: "POST",
    headers: {
      Authorization: `Key ${process.env.REFABRIC_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": request.headers.get("Idempotency-Key") ?? crypto.randomUUID(),
    },
    body: await request.text(),
  });
  return new Response(await r.text(), { status: r.status, headers: { "Content-Type": "application/json" } });
}
```

## What your proxy must check

| Check | Why |
|---|---|
| **Task allowlist** | forward only the tasks your app uses; everything else is refused by you |
| **End-user authentication** | only your signed-in users reach the proxy |
| **Per-user rate limit** | one user cannot spend your whole Refabric rate or balance |
| **Job ownership** | a user reads only the jobs they started — keep the `job_id` with your user |
| **Body size** | refuse bodies larger than your app sends |

:::warning
An open proxy spends your credits. Anyone who finds a proxy without authentication and an allowlist
can run any task on your balance. Every job it starts is yours, charged to your account.
:::

## Related

::::cards
:::card{title="Client setup" href="/task-apis/calling-tasks/client-setup"}
The server-side client the proxy uses.
:::
:::card{title="Security" href="/api-reference/platform/security"}
How to store and roll keys.
:::
:::card{title="Limits" href="/task-apis/limits"}
The request rate your proxy shares across all users.
:::
::::
