For AI agents: this page is also available as Markdown at https://docs.refabric.com/task-apis/calling-tasks/proxy-setup.md, and the index of every page is https://docs.refabric.com/llms.txt.

Task APIs › Calling tasks

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

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)

// 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

CheckWhy
Task allowlistforward only the tasks your app uses; everything else is refused by you
End-user authenticationonly your signed-in users reach the proxy
Per-user rate limitone user cannot spend your whole Refabric rate or balance
Job ownershipa user reads only the jobs they started — keep the job_id with your user
Body sizerefuse bodies larger than your app sends

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.