---
title: "Start a processing job"
method: POST
path: "/api/job"
tags: ["Hide", "Reveal"]
---

# Start a processing job

`POST /api/job`

## Request

- Base URL: `https://snapwonders.com`
- URL: `https://snapwonders.com/api/job`
- Auth: API key in header `X-Api-Key`

Submits options and dispatches the steganography job to the worker queue. All files must be fully uploaded via TUS before calling this endpoint — verify with `GET /api/session/{uid}/files`.

**Hide jobs** accept the full set of encoding options below. **Reveal jobs** only use `upload_uid`, `password`, and `expiry` — all encoding keys are ignored.

The call is **idempotent**: submitting options for a session that already has a job returns the existing job without creating a duplicate.

**Authentication:** `X-Api-Key` header required.

## Request body

- object
  - `upload_uid` string, uuid, required: The `upload_uid` returned by `POST /api/session`
  - `password` string, required: Encryption password. Required for both hide and reveal. Must be at least 8 characters with mixed case, a digit, and a symbol.
  - `expiry` '1h' | '12h' | '1d' | '2d' | '1w' | '1m', required: How long the result download link remains accessible after the job completes. `1h` = 1 hour, `12h` = 12 hours, `1d` = 1 day (default), `2d` = 2 days, `1w` = 1 week, `1m` = 1 month. **Free-tier keys are silently clamped** to `upload.free_max_expiry` (default `1d`): the job still succeeds, but anything longer is reduced. Pro accounts get the requested value. No response currently returns the granted expiry, so assume the free maximum unless the owning account is Pro.
  - `output_format` 'avif' | 'heic' | 'jp2' | 'jxl' | 'png' | 'webp': **Hide only.** Image format for stego output files. All formats are lossless. Default: `avif`.
  - `packing_strategy` 'spread' | 'tight' | 'quality': **Hide only.** How secrets are distributed across covers. `spread`: each cover altered as little as possible (default). `tight`: pack multiple secrets per cover to minimise cover count. `quality`: preserve visual quality above all else.
  - `scale_secret_to_fit` boolean: **Hide only.** When a secret is too large to hide in its cover, automatically reduce it (resize images, increase video compression) until it fits. Set to `false` to fail instead of degrading quality. Default: `true`.
  - `strip_cover_metadata` boolean: **Hide only.** Remove EXIF, GPS, and all other embedded metadata from the stego output cover. Recommended when privacy matters. Default: `false`.
  - `strip_secret_metadata` boolean: **Hide only.** Remove EXIF, GPS, and embedded metadata from secret files before they are hidden. Reduces payload size. Default: `false`.
  - `video_output_format` 'ffv1_mkv' | 'huffyuv_avi' | 'lagarith_avi' | 'magicyuv_avi' | 'prores4444_mov' | 'prores4444xq_mov' | 'av1lossless_mkv': **Hide only, video covers.** Lossless codec and container for stego video output. Default: `ffv1_mkv`.
  - `secret_video_crf` integer: **Hide only, video secrets.** Compression quality for secret videos before hiding (lower = better quality, larger payload). Default: `28`.
  - `secret_video_codec` 'h264' | 'h265': **Hide only, video secrets.** Codec used to compress secret videos. `h265` gives ~40% smaller payloads. Default: `h265`.
  - `vault_profile_id` integer: **Pro accounts only, hide jobs only.** ID of a Vault Profile to use as an additional key factor in the steganographic key derivation. Vault profiles are created and managed at `/vault` in the web UI. Providing this on a free account returns HTTP 402.

## Response `200`

Job already exists (idempotent repeat call) — returns existing job

## Other responses

- `201`: Job queued
- `400`: Validation error — weak password, no files uploaded, invalid option value, etc.
- `401`: Not authenticated. `error` names the exact cause: `missing_api_key`, `malformed_api_key`, `invalid_api_key` (key unknown/revoked/expired), or — when the key itself is valid — `account_not_verified` / `account_inactive`.
- `402`: Pro account required — `vault_profile_id` is only available on Pro plans
- `403`: Two distinct causes, named by `error`. `forbidden` — the session does not belong to your API key. `account_blocked` — an administrator has blocked this account from submitting jobs; permanent until reversed by support, so do not retry. `account_blocked` is a different axis from the 401 account-status codes: the account may be fully active and verified and still be job-blocked.
- `404`: Session not found
- `410`: Session expired — sessions are valid for 24 hours from creation
- `429`: Too many active jobs for this API key. Wait for queued/processing jobs to finish, then retry. Response includes `active_jobs` and `limit`.

---

[API](https://skmtc.dev/snapwonders/apis/snapwonders-api-mcp.md) · [All operations](https://skmtc.dev/snapwonders/apis/snapwonders-api-mcp/llms.txt) · [OpenAPI document](https://skmtc.dev/snapwonders/apis/snapwonders-api-mcp/revisions/645bbccc9550?raw)
