Files
AI-Horde/README_integration.md
tazlin ac541c7ae4 feat(api): return the original submission from the request endpoints
The request endpoints added for the submitting key returned WaitingPrompt.prompt,
which is the worker input after the replacement filter has rewritten it. A
client could diff its own submission against the response and learn exactly
what the filter replaces, which cannot be allowed. The original submission
is now stored on its own column and returned to workers, but not to users
querying their own request details.

- Add WaitingPrompt.submitted_prompt, the parsed submission before
  styles or moderation. It is nullable, deferred, and raiseload, so ordinary
  queries, joins and relationship loads never fetch it, and an accidental lazy
  access raises instead of silently pulling the text into worker payloads,
  webhooks or share metadata
- Capture it in GenerateTemplate._post_inner before the subclass validate()
  applies styles, and pass it into every waiting prompt constructor. The
  constructor commits, so setting it afterwards would leave a committed row
  without provenance
- Read only through read_privileged_submitted_prompt(purpose=...), a scalar
  select that leaves the attribute unloaded on the instance. SubmittedPromptPurpose
  is a required keyword identifying the reading consumer, so every caller that
  handles the text is findable by grep
- get_submitted_request returns the original prompt. A row without provenance
  omits the prompt entirely. The effective prompt is never a fallback because
  that would reopen the leak for rows the filter touched
- Both request endpoints send Cache-Control: private, no-store
- ALTER waiting_prompts in sql_statements/5.1.12.txt: nullable, no default, so
  the add is metadata-only, under a 5s lock_timeout that fails instead of
  queueing an ACCESS EXCLUSIVE lock behind a long transaction. Existing rows
  stay unknown; provenance is never backfilled from prompt
- Document the contract in docs/reference/prompt_provenance.md, and cover
  deferral, the privileged read, the original surviving a filter rewrite, the
  omission on legacy rows and absence from every existing export in tests, with
  a migration test that the script is additive and repeatable
2026-10-01 18:10:04 -04:00

7.0 KiB

Integrating to the AI Horde

This readme will provide information on how you can build software which uses the AI Horde

REST API

First of all, we provide a fully open and self-documented REST API for all Generative AI functions we support.

Complete Documentation.

SDK

We have multiple SDK for the AI horde

The Basics

The workflow to use the AI horde is fairly straightforward.

First choose what kind of generation you want to have. Image, Text, or Alchemy (Image interrogation/manipulation). Depending on which one you'll use, you have to then use a specific set of endpoints.

They all work the same way. First send the payload which describes the generation you want to have. This is specific to the type of generation you want. This will return information about this request and will also even inform you if this request is even possible. You will receive an "id" here which you need to store to query in the next steps.

Once the request is accepted and in-progress, periodically check its status (the horde has a 1 second cache on status so there's no purpose checking more often). The status endpoint will inform you if it's done, how many underlying jobs are still in progress, waiting, or done.

If the request is done, retrieve the content from the generations key. This will either be a URL from which to download an image, or the resulting generation if it's text.

You can get the results faster by using the webhooks.

Image Generation Endpoints

Use these endpoints to generate images. Please use the /check endpoint for checking if a request is done, and the statud endpoint for retrieving the full results.

  1. Initiate the request: api/v2/generate/async
  2. Check the request status: api/v2/generate/check
  3. Retrieve the request results: api/v2/generate/status
  4. Retrieve the submitted parameters: api/v2/generate/request (see Request parameters)

Text Generation Endpoints

Use these endpoints to generate text.

  1. Initiate the request: api/v2/generate/text/async
  2. Retrieve the request results: api/v2/generate/text/status
  3. Retrieve the submitted parameters: api/v2/generate/text/request (see Request parameters)

Image Alchemy

Use these endpoints to interrogate or manipulate images.

  1. Initiate the request: api/v2/interrogate/async
  2. Retrieve the request results: api/v2/interrogate/status

Request parameters

The check and status endpoints only need the request ID, so anyone holding the ID can follow a request's progress and collect its results. The submitted prompt and parameters are not part of those responses. To read them back, call api/v2/generate/request/<id> (or api/v2/generate/text/request/<id>) with the apikey header set to the key that submitted the request. A different user's key is refused, and so is the anonymous key. A shared key can read the requests it submitted, and the owner of a shared key can read the requests submitted through it.

The response uses the generation input shape, but is not an exact replay: prompt is the original submitted string, before styles and moderation, while parameters include defaults and style overrides. Legacy requests without a recorded original omit prompt; the effective worker prompt is never used as a fallback. Clients must retain the original style/submission to reproduce a styled request. Options that only steer submission (dry_run, allow_downgrade, replacement_filter, style) are not returned. Source images come back as uploaded storage references, not submitted base64 data. Responses use Cache-Control: private, no-store.

Shared key activity and privacy

Account details (find_user and users/<id>) list only directly owned requests in active_generations. Requests funded by a shared key, including through a style, are excluded even from privileged account views. Possession of a shared key does not provide a list of its request IDs. Access using an already-known request ID is unchanged, including the owner's ability to read its submitted parameters.

To monitor a shared key, call GET /api/v2/sharedkeys/<id> with the owner's personal API key in the apikey header. The response adds active_usage, with image and text summaries. Each contains:

  • requests: number of requests with queued or processing work.
  • queued and processing: generation counts, not request counts.
  • finished: completed generations within those active requests.
  • oldest_queued_age: age in seconds of the oldest request with queued work, or zero if none.

Inactive, expired, faulted, cancelled, and fully completed requests are excluded. Empty summaries contain zeroes. Existing kudos and utilized fields describe the remaining allowance and cumulative spending; they are not estimates of the cost of outstanding work. No request IDs or request contents are included. The activity summary is omitted for unauthenticated callers, shared-key holders, and other accounts. Owner-authenticated responses bypass the public endpoint cache and must not be stored by HTTP caches.

Errors and return codes

Whenever the AI Horde encounters an issue with an operation, it will return the usual HTTP code, along with a json containing information about the error. Please see the dedicated README for more info

Webhooks

The AI Horde supports sending back the final generations as soon as they're delivered, using webhooks.

To use the webhooks for generation, submit the URL to which the POST for the webhook should be delivered in the webhook key during submit.

As each job is fulfilled, a payload will be sent to that webhook, containing similar information you would receive from the status endpoint.

Below you will find the webhook json payload for each type of generation

Image

{
  "img": "<IMAGE URL: STR>",
  "seed": <IMAGE SEED: INT>,
  "worker_id": "<WORKER ID: STR>",
  "worker_name": "<WORKER NAME: STR>",
  "model": "<MODEL NAME: STR>",
  "id": "<JOB ID: STR>",
  "gen_metadata": <GENERATION METADATA: LIST[DICT]>,
  "request": "<REQUEST ID: STR>",
  "kudos": <KUDOS CONSUMED: INT>
}

Text

  "text": "<TEXT URL: STR>",
  "seed": <TEXT SEED: INT>,
  "worker_id": "<WORKER ID: STR>",
  "worker_name": "<WORKER NAME: STR>",
  "model": "<MODEL NAME: STR>",
  "id": "<JOB ID: STR>",
  "gen_metadata": <GENERATION METADATA: LIST[DICT]>,
  "request": "<REQUEST ID: STR>",
  "kudos": <KUDOS CONSUMED: INT>
}

Alchemy

  "form": "<FORM TYPE: STR>",
  "state": <FORM STATE: STR>,
  "result": "<FORM RESULTS: DICT>",
  "worker_id": "<WORKER ID: STR>",
  "worker_name": "<WORKER NAME: STR>",
  "id": "<JOB ID: STR>",
  "request": "<REQUEST ID: STR>",
  "kudos": <KUDOS CONSUMED: INT>
}