Your own pipeline and local development

Send runs to your own code and answer them, and build it all on gemmein dev.

Your own pipeline

What it is. A job tool whose provider is external. The work runs on your compute, at a URL you name, in place of a provider Gemmein calls for you.

Does.

  • Starting a run POSTs it to your URL, signed, on Gemmein's own retry schedule, the same way a job goes to Replicate.
  • Needs no provider key: the provider-key endpoint refuses external, because your pipeline is the provider. No alias, and model is your own label or nothing.
  • Keeps the run on Gemmein: its reservation, its person, its deadline, its result. No relay row is created for it.
  • Works with any code behind that URL, in any language or framework: a Python script, a LangGraph graph, a CrewAI crew, an n8n flow, a multi-agent system or a person in the middle. It counts as one tool, with one run and one price.
  • Sends it the run signed with the person, the inputs, a credit ceiling and a deadline of up to 24 hours. The work runs on your own servers.

Does not.

  • Need a relay or create a relay row. run_started fires react-only if you have one, but nothing requires it.
  • Hand your pipeline a provider key or a model. There is none.
  • Bill past bounds.deadlineMinutes (1 to 1,440; 60 when unset; only an external tool may set it). Past it the run expires and the hold returns, whatever your pipeline is doing.
  • Accept a second complete, a complete after fail, or a call past the deadline. The token completes once.

Needs something else when.

  • Your work finishes inside the request → a chat tool, or a job on a provider Gemmein already calls (Replicate, fal, …). No pipeline needed.
  • You want the tool free for subscribers → credits: 0 with requiresAny set (a priced-or-included tool; see credits in the field table). 0 with no requiresAny is refused at save.

Example. A weekly report generator: url points at your own function, bounds.deadlineMinutes: 30, credits: 0 with requiresAny: ["access:pro", "access:max"]. It is included with either plan, still metered on the ledger, never charged.

A research crew (a planner, three researchers and a writer agent, in whichever framework you use) is the same tool. url points at the crew's own endpoint, bounds.deadlineMinutes: 120, and it completes with the written report in text once every agent in the crew has run (run files hold images, audio and video).

A start with no url set answers 409 tool_incomplete ("… names no URL — set where it runs") before any credit moves.

Agents

A relay's start_run starts this tool, or any tool, for that person when a record is written, a payment lands, a clock fires or a webhook arrives, with nobody pressing a button. run_started chains it to the next tool, one hop at a time, in a sequence your app composes. Gemmein owns no graph and runs none of the agent's code. It holds the run's reservation, person, deadline and result, and nothing else.

The hand-off

Gemmein signs the hand-off the same way it signs a relay's call_url. The two envelopes are what Gemmein sends your URL and what your pipeline sends back:

// Gemmein → your URL (X-Gemmein-Signature: t=…,v1=…)
{
  "run": { "id": "run_…", "tool": "poster", "kind": "generate", "units": "images", "reserved": 8, "createdAt": "…" },
  "params": { "prompt": "…" }, "person": { "id": "usr_…", "email": "…" }, "deadline": "…",
  "complete": { "url": "…/complete", "token": "…", "expiresAt": "…" },
  "progress": { "url": "…/progress" }, "fail": { "url": "…/fail" }
}

// your URL → Gemmein (Authorization: Bearer <token>)
{ "text": "made: a lighthouse at dusk", "units": 2 }

Answer a run

Your pipeline answers with two required calls and one optional one, all with the token the hand-off carried:

EndpointBodyAnswers
POST …/complete{ units?, files?: [{ url, contentType? }], text?, data? }, up to 64 KB200 { run }: succeeded, settled at units, never above the hold
POST …/fail{ error? }, up to 2,000 characters200 { run }: failed, the hold returned
POST …/progress (optional){ progress: 0..100, note? }, any number of times200 { run }: non-spending, never takes the lease

The pipeline helper

g.pipelines.handle does all of this in three lines (@gemmein/sdk ≥ 0.12.0):

export default g.pipelines.handle(process.env.PIPELINE_SECRET!, async (run, { progress }) => {
  await progress(50, "half way");
  return { text: `made: ${run.params.prompt}` };
});

g.pipelines.handle(secret, fn, options?) verifies the signature, acknowledges within Gemmein's 10-second window, runs fn in the background, and calls /complete or /fail for you. It is a plain Fetch-API handler that runs unmodified on Vercel, Cloudflare Workers, Deno, Bun or Node 18+.

secret is the tool's own signing secret (aisk_…), created with the tool, shown once on the tool's page and rotatable. It is never your app key. A bad or stale signature is refused before fn runs. A re-delivered hand-off (the same run.id, after a lost acknowledgement) runs fn once.

Local development

gemmein dev answers AI tools without a key: a fake provider echoes a stream, marked x-gemmein-ai: fake, and the tool's credits are spent from the local ledger. For a real call, set GEMMEIN_AI_KEY_OPENAI, GEMMEIN_AI_KEY_ANTHROPIC or GEMMEIN_AI_KEY_GOOGLE in the environment gemmein dev runs in. Keys never sync; your hosted app holds its own, pasted on the AI page.

Tools do sync. A file at gemmein/ai/tools/<name>.json hot-reloads like a relay, and the boot card prints a line per tool: AI tools: deep-research (20 credits · requires access:pro-max · openai).

Credits in Development

A fresh person's balance is zero, and AI tools refuse at zero locally and on Gemmein alike, so testing starts with a credit arriving. Locally that is the pay simulator. Declare a product with "Grants credits" in gemmein/payments.json (npx gemmein payments setup asks for it), call g.payments.buy("credits pack"), and open the URL it returns. The simulated payment travels the real signed-webhook path, and the credits land on that person's local ledger.

A relay's grant_credits is the other local way, for a receiver you have wired. On Gemmein the two ways are the same purchase, plus a comp you add by hand on that person's page under Customers. There is no comp in gemmein dev, because the dashboard is not part of it.

Runs and pipelines in Development

On gemmein dev without a key for the provider, a run ends succeeded with a generated image or a placeholder transcript. It is metered and settled, so the whole flow works before any key exists.

An external tool works the same way on gemmein dev, with loopback allowed. Your pipeline calls http://127.0.0.1:<port>/runs/<app>/<env>/<id>/complete with the token.

Was this page helpful?

Last updated