# FlashBoards Public API v1

A single JSON endpoint for using FlashBoards accounts, boards, model discovery, and generation from your own server with an API key.

## Endpoint

- Method: GET / POST
- URL: https://flashboards.yaroflasher.com/api/v1/operations
- Auth: `Authorization: Bearer fb_sk_…`

## Operations

- `get_account`
- `list_shared_boards`
- `create_media_operation`
- `get_media_operation`
- `cancel_media_operation`
- `retry_media_operation`
- `list_generation_models`
- `estimate_generation_cost`
- `generate_image`
- `generate_video`
- `get_generation`
- `list_recoverable_generations`
- `prepare_board_generation`
- `generate_on_board`
- `list_board_sessions`
- `get_board_snapshot`
- `add_media_to_board`
- `execute_board_action`

## Get a key

Create and revoke keys in Settings → Developer. The full key is shown once. Keep it on a server: this API is headless, sends no CORS headers, and a key in browser code is a key you have given away.

## Call an operation

POST the operation name and its input. Operations that take no arguments can be called with the name alone.

```bash
curl -X POST https://flashboards.yaroflasher.com/api/v1/operations \
  -H "Authorization: Bearer ${FLASHBOARDS_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"operation":"get_account"}'
```

## Discover what is available

GET the same URL for the current operations, each with a title, a description, and a JSON Schema for its input. Discovery is authenticated like any other call.

- The published schemas are advisory, not complete. Cross-field rules and count limits are enforced when the request is parsed, not described in the schema — a request that satisfies the schema can still be rejected.
- Unknown input keys are removed rather than refused, so the schemas do not forbid extra properties.

## Read the response

Every accepted call answers 200 with { ok, operation, result }. `ok` is a transport-level flag: it mirrors whether the operation reported itself as failed.

- `ok: true` does not always mean the work succeeded. `generate_on_board` reports rejected and indeterminate outcomes as ordinary results — always read `result.status` for it.
- Business failures stay 200. Only transport problems use other codes: 401 for a bad key, 415 for a wrong content type, 400 for a malformed or oversized body, 429 when the rate limit is hit, 503 when the service is briefly unavailable, and 500 for an unexpected internal failure.
- The rate limit applies per account. A 429 carries Retry-After.

## Use a local reference image

`generate_image` can accept one explicitly approved local image in `referenceImages`. This is part of the same generation call: it does not require a board, an open tab, or a separate upload operation.

```json
{
  "operation": "generate_image",
  "input": {
    "prompt": "Describe the requested edit",
    "model": "nano-banana-2",
    "resolution": "1k",
    "generationAttemptId": "attempt-local-reference-1",
    "referenceImages": [
      {
        "filename": "reference.png",
        "dataUrl": "data:image/png;base64,..."
      }
    ]
  }
}
```

- The attachment must be a supported image MIME type encoded as a bounded base64 data URL. Send the image bytes only after the user has approved that attachment; do not send a local filesystem path.
- FlashBoards stores the input temporarily and passes its short-lived signed URL into the generation. The upload handle is not a separate public resource and is not returned in the generation result.
- `imageUrls` remains supported for existing URL-based reference flows. `referenceImages` is the current local-image slice; local video/audio attachments and local references for `generate_video` are not supported yet.

## Spending money safely

Paid operations have two rules that matter more than the rest.

- `generate_image` and `generate_video` require `generationAttemptId`. Reusing the same value is what makes a retry safe — retrying with a NEW value starts a second paid attempt.
- If a start returns `SUBMISSION_INDETERMINATE` (with `ok: false`), or `generate_on_board` returns `indeterminate_after_claim` (with `ok: true`), the attempt may already have been charged. Do not retry. The attempt is recorded on our side for reconciliation.
- A per-call spend ceiling applies to every programmatic caller. Accounts with unlimited generation access are not denied for balance, but the ceiling still applies to them.
- Call `estimate_generation_cost` first when you want the price before committing.

## Recover a generation after losing its ID

Call `list_recoverable_generations` to find your recent recoverable generations and receive a fresh `generationId` for each. Then call `get_generation` with that ID for one status/result check.

- The list covers the last 48 hours and never starts, refreshes, or charges a generation.
- Only generations supported by the current Public API model catalog are returned. The response never exposes provider task IDs or internal database IDs.

## Working with a board

The board operations act on a FlashBoards tab that is open and signed in. With no live tab they return an error saying so; `list_board_sessions` reports an empty list.

## More help

- [Generation billing](https://flashboards.yaroflasher.com/help/generation-billing)
- [FlashBoards Help](https://flashboards.yaroflasher.com/help)
