# Nightjar Public API v1: Complete Guide

> Use Nightjar fully programmatically without visiting the web app.

**Base URL:** https://api.nightjar.so/v1

**Contract version:** 1.0.0

**Canonical OpenAPI:** https://docs.nightjar.so/openapi.yaml

## Authentication and Teams

Every API Credential acts as one Team and uses one permission profile: read-only or full. An active Nightjar Subscription is required for creative writes. The Team owns its Library, Products, Credits, and effective public-API concurrency budget.

## Uploads and Assets

Create an Upload Session with the exact content type and byte length, POST raw image bytes to its short-lived signed URL with every returned header, then exchange the upload token at the Session's complete endpoint. Never send the API Credential to the signed upload URL. The resulting Asset has a stable public-by-link URL while it exists.

## Products and reusable ingredients

Products group one or more Product Photo Assets and identify one Primary Product Photo. Product Photos are many-to-many: one Asset may belong to several Products. Photography Styles, Backgrounds, Poses, and Fashion Models are reusable ingredients. Metadata-only ingredient changes are synchronous and free; work that analyzes or generates media returns an Operation.

## Operations

Authoring Operations are asynchronous and cannot be canceled. Poll until completed or failed. Creation failure leaves no partial resource; update failure preserves the previous version. A source-changing Operation blocks PATCH and DELETE for that resource until it finishes.

## Generations and Credits

Generation admission reserves the planned Credits and allocates stable output slots. Poll until every output is terminal. A Generation is completed when at least one output succeeds and failed when every output fails. Only completed outputs are charged. Product Photography costs one Credit per generated image; Edit Images and Upscale follow their documented fixed costs.

## Reliability

Every side-effecting admission requires an Idempotency-Key. Same key and normalized intent replay the original admission; changed intent returns 422. List endpoints use opaque cursor pagination. Honor Retry-After on every 429 and retry with exponential backoff and jitter. DELETE is permanent and has no restore endpoint.

## Compatibility

Breaking changes require a new major path version. Additive endpoints and optional fields may ship inside v1, so clients must ignore unknown response fields. Superseded stable major versions receive at least 24 months of support except where an urgent security or legal requirement makes that unsafe.


## Error catalog

Errors use `application/problem+json`. Branch on the stable `code`, not the human title or detail. The `request_id` matches the Request-Id response header.

| HTTP | Code | Meaning | Recovery |
| --- | --- | --- | --- |
| 400 | `invalid_request` | Malformed JSON, unsupported query parameters, or a missing required header. | Correct the request before retrying. |
| 400 | `invalid_cursor` | The pagination cursor is invalid for this collection or filter set. | Restart pagination without the cursor. |
| 401 | `authentication_required` | The API Credential is missing or invalid. | Supply a valid Bearer credential. |
| 401 | `credential_expired` | The API Credential reached its configured expiry. | Create a replacement credential in Settings. |
| 401 | `credential_revoked` | The API Credential was revoked. | Use a different active credential. |
| 402 | `insufficient_credits` | The Team cannot reserve enough Credits for this work. | Add Credits, then submit a new logical request. |
| 403 | `permission_denied` | The credential profile cannot perform this operation. | Use a full credential for writes. |
| 403 | `subscription_required` | Creative writes require an active Nightjar Subscription. | Restore the Team Subscription; reads remain available. |
| 404 | `not_found` | The resource does not exist or is not visible to this Team. | Treat the resource as unavailable. |
| 404 | `upload_not_found` | The Upload Session is unavailable. | Create a new Upload Session. |
| 404 | `asset_not_found` | The Asset is unavailable to this Team. | Use a live Asset from the Team Library. |
| 404 | `product_not_found` | The Product is unavailable to this Team. | Use a live Product from the Team Library. |
| 409 | `idempotency_in_progress` | The same Idempotency-Key is still being admitted. | Wait, then replay the same key and intent. |
| 409 | `resource_operation_in_progress` | The resource already has source-changing authoring work in progress. | Poll active_operation_id before retrying. |
| 409 | `upload_bytes_conflict` | Different bytes were sent to an Upload Session that already accepted bytes. | Create a new Upload Session for the different file. |
| 410 | `upload_expired` | The direct-upload window expired. | Create a new Upload Session. |
| 422 | `invalid_input` | A field or field combination violates the documented request contract. | Correct the reported fields before retrying. |
| 422 | `invalid_reference` | A referenced Asset, Product, or ingredient is not usable by this Team. | Replace the invalid reference. |
| 422 | `idempotency_key_reused` | The key was already used for a different normalized request intent. | Use the original intent or a new key. |
| 422 | `already_at_target_resolution` | The Asset already meets or exceeds the requested Upscale target. | Use the Asset as-is or request a higher target. |
| 429 | `concurrency_limit_reached` | The Team reached its public-API creative-work concurrency budget. | Honor Retry-After and wait for existing work to finish. |
| 429 | `rate_limited` | The request rate exceeded the per-credential or Team limit. | Honor Retry-After and retry with backoff. |
| 503 | `service_unavailable` | Nightjar temporarily paused new API write admission. | Honor Retry-After. Reads and already accepted work remain available. |
| 500 | `internal_error` | Nightjar could not complete the HTTP request. | Retry transient failures and include Request-Id if support is needed. |


# Endpoint reference

## Team

Team Credits and API capabilities.

### GET /v1/team

Retrieve the credential's Team

**Operation ID:** `retrieveTeam`

#### Parameters

No request parameters.

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/team"
```

#### Successful response

**200**: The credential, its Team, Credits, and effective API capabilities.

```json
{
  "object": "team",
  "id": "string",
  "name": "string",
  "credits": 0,
  "credential": {
    "object": "api_credential",
    "id": "string",
    "name": "string",
    "prefix": "string",
    "profile": "read_only",
    "expires_at": "2026-08-16T10:00:00.000Z"
  },
  "capabilities": {
    "api_access": false,
    "subscription_active": false,
    "creative_writes_allowed": false,
    "api_concurrency": {
      "limit": 0,
      "used": 0
    }
  }
}
```

**Declared statuses:** `200`, `401`, `429`, `default`

## Uploads

Direct-upload session creation and finalization.

### POST /v1/uploads

Create an Upload Session

**Operation ID:** `createUpload`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `content_type` | body | `image/jpeg \| image/png \| image/gif \| image/webp \| image/avif` | Yes | Accepted source formats match the web app. Nightjar normalizes the stored Asset to jpeg, png, or webp. |
| `size_bytes` | body | `integer` | Yes | Raw source byte length. The maximum is 70 MiB. |

#### Request body example

```json
{
  "content_type": "image/jpeg",
  "size_bytes": 184320
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"content_type":"image/jpeg","size_bytes":184320}' \
  "https://api.nightjar.so/v1/uploads"
```

#### Successful response

**201**: One short-lived Upload Session. No Asset exists yet.

```json
{
  "object": "upload",
  "id": "string",
  "status": "awaiting_upload",
  "upload": {
    "method": "POST",
    "url": "https://example.com/resource",
    "headers": {},
    "expires_at": "2026-08-16T10:00:00.000Z",
    "response": {
      "content_type": "application/json",
      "body": {
        "upload_token": "string"
      }
    }
  }
}
```

**Declared statuses:** `201`, `400`, `401`, `403`, `409`, `422`, `429`, `503`, `default`

### POST /v1/uploads/{upload_id}/complete

Finalize an Upload Session into an Asset

**Operation ID:** `completeUpload`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `upload_id` | path | `string` | Yes |  |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `upload_token` | body | `string` | Yes |  |

#### Request body example

```json
{
  "upload_token": "upl_token_from_direct_upload"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"upload_token":"upl_token_from_direct_upload"}' \
  "https://api.nightjar.so/v1/uploads/upl_example/complete"
```

#### Successful response

**201**: The complete usable Asset created by finalization.

```json
{
  "object": "asset",
  "id": "ast_example",
  "media_type": "image",
  "source": "upload",
  "source_generation_id": null,
  "format": "jpeg",
  "width": 1200,
  "height": 800,
  "size_bytes": 184320,
  "url": "https://assets.nightjar.so/team/example.jpg",
  "created_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `201`, `400`, `401`, `403`, `404`, `409`, `410`, `422`, `429`, `503`, `default`

## Assets

Durable uploaded and generated images.

### GET /v1/assets

List Team Assets

**Operation ID:** `listAssets`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No |  |
| `after` | query | `string` | No |  |
| `source` | query | `upload \| generation` | No |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/assets"
```

#### Successful response

**200**: Team Assets ordered by created_at desc, id desc.

```json
{
  "data": [
    {
      "object": "asset",
      "id": "ast_example",
      "media_type": "image",
      "source": "upload",
      "source_generation_id": null,
      "format": "jpeg",
      "width": 1200,
      "height": 800,
      "size_bytes": 184320,
      "url": "https://assets.nightjar.so/team/example.jpg",
      "created_at": "2026-08-16T10:00:00.000Z"
    }
  ],
  "page": {
    "has_more": false,
    "next_cursor": "string"
  }
}
```

**Declared statuses:** `200`, `400`, `401`, `403`, `429`, `default`

### GET /v1/assets/{asset_id}

Retrieve an Asset

**Operation ID:** `retrieveAsset`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `asset_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/assets/ast_example"
```

#### Successful response

**200**: One active Asset.

```json
{
  "object": "asset",
  "id": "ast_example",
  "media_type": "image",
  "source": "upload",
  "source_generation_id": null,
  "format": "jpeg",
  "width": 1200,
  "height": 800,
  "size_bytes": 184320,
  "url": "https://assets.nightjar.so/team/example.jpg",
  "created_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `429`, `default`

### DELETE /v1/assets/{asset_id}

Delete an Asset

**Operation ID:** `deleteAsset`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `asset_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request DELETE \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/assets/ast_example"
```

#### Successful response

**200**: Deleted from authenticated API and UI use. Every Product Photo membership is removed; affected nonempty Products receive a deterministic replacement Primary Product Photo, and only Products left empty are deleted. Historical Generation output references remain and may resolve to 404.

```json
{
  "object": "asset_deletion",
  "id": "ast_example",
  "removed_product_memberships": 2,
  "reassigned_primary_product_ids": [
    "prd_surviving"
  ],
  "deleted_product_ids": [
    "prd_left_empty"
  ]
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `409`, `429`, `503`, `default`

## Products

Reusable collections of Product Photos.

### GET /v1/products

List Products

**Operation ID:** `listProducts`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No |  |
| `after` | query | `string` | No |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/products"
```

#### Successful response

**200**: Team Products ordered by created_at desc, id desc.

```json
{
  "data": [
    {
      "object": "product",
      "id": "prd_example",
      "name": "Field Bag",
      "description": "Waxed canvas with leather handles",
      "dimensions": "40 × 28 × 12 cm",
      "asset_ids": [
        "ast_example_front",
        "ast_example_detail"
      ],
      "primary_asset_id": "ast_example_front",
      "created_at": "2026-08-16T10:00:00.000Z",
      "updated_at": "2026-08-16T10:00:00.000Z"
    }
  ],
  "page": {
    "has_more": false,
    "next_cursor": "string"
  }
}
```

**Declared statuses:** `200`, `400`, `401`, `403`, `429`, `default`

### POST /v1/products

Create a Product

**Operation ID:** `createProduct`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | Yes |  |
| `description` | body | `string` | No |  |
| `dimensions` | body | `string` | No |  |
| `asset_ids` | body | `string[]` | Yes |  |
| `primary_asset_id` | body | `string` | Yes | Must be present in asset_ids. |

#### Request body example

```json
{
  "name": "Field Bag",
  "description": "Waxed canvas with leather handles",
  "dimensions": "40 × 28 × 12 cm",
  "asset_ids": [
    "ast_example_front",
    "ast_example_detail"
  ],
  "primary_asset_id": "ast_example_front"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Field Bag","description":"Waxed canvas with leather handles","dimensions":"40 × 28 × 12 cm","asset_ids":["ast_example_front","ast_example_detail"],"primary_asset_id":"ast_example_front"}' \
  "https://api.nightjar.so/v1/products"
```

#### Successful response

**201**: Product created synchronously and atomically.

```json
{
  "object": "product",
  "id": "prd_example",
  "name": "Field Bag",
  "description": "Waxed canvas with leather handles",
  "dimensions": "40 × 28 × 12 cm",
  "asset_ids": [
    "ast_example_front",
    "ast_example_detail"
  ],
  "primary_asset_id": "ast_example_front",
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `201`, `401`, `403`, `409`, `422`, `429`, `503`, `default`

### GET /v1/products/{product_id}

Retrieve a Product

**Operation ID:** `retrieveProduct`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `product_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/products/prd_example"
```

#### Successful response

**200**: One Product.

```json
{
  "object": "product",
  "id": "prd_example",
  "name": "Field Bag",
  "description": "Waxed canvas with leather handles",
  "dimensions": "40 × 28 × 12 cm",
  "asset_ids": [
    "ast_example_front",
    "ast_example_detail"
  ],
  "primary_asset_id": "ast_example_front",
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `429`, `default`

### PATCH /v1/products/{product_id}

Update a Product

**Operation ID:** `updateProduct`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `product_id` | path | `string` | Yes |  |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | No |  |
| `description` | body | `string` | No |  |
| `dimensions` | body | `string` | No |  |
| `asset_ids` | body | `string[]` | No |  |
| `primary_asset_id` | body | `string` | No |  |

#### Request body example

```json
{
  "name": "Field Bag · Navy",
  "asset_ids": [
    "ast_example_front",
    "ast_example_navy"
  ],
  "primary_asset_id": "ast_example_navy"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request PATCH \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Field Bag · Navy","asset_ids":["ast_example_front","ast_example_navy"],"primary_asset_id":"ast_example_navy"}' \
  "https://api.nightjar.so/v1/products/prd_example"
```

#### Successful response

**200**: Updated synchronously and atomically.

```json
{
  "object": "product",
  "id": "prd_example",
  "name": "Field Bag",
  "description": "Waxed canvas with leather handles",
  "dimensions": "40 × 28 × 12 cm",
  "asset_ids": [
    "ast_example_front",
    "ast_example_detail"
  ],
  "primary_asset_id": "ast_example_front",
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `409`, `422`, `429`, `503`, `default`

### DELETE /v1/products/{product_id}

Delete a Product

**Operation ID:** `deleteProduct`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `product_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request DELETE \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/products/prd_example"
```

#### Successful response

**204**: Permanently removed from API and UI use. Product Photo relationships dissolve; Assets and historical Product Attribution remain.

No response body.

**Declared statuses:** `204`, `401`, `403`, `404`, `409`, `429`, `503`, `default`

## Photography Styles

Reusable visual style ingredients.

### GET /v1/photography-styles

List Photography Styles

Returns every usable Team-owned and global resource by default, ordered together by created_at desc, id desc. Use scope to narrow the collection.

**Operation ID:** `listPhotographyStyles`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No |  |
| `after` | query | `string` | No |  |
| `scope` | query | `all \| team \| global` | No | Omit for every resource the Team can use, or filter to Team-owned or global premade resources. |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/photography-styles"
```

#### Successful response

**200**: Team and premade Photography Styles.

```json
{
  "data": [
    {
      "object": "photography_style",
      "id": null,
      "scope": null,
      "name": null,
      "source_asset_ids": null,
      "created_at": null,
      "updated_at": null,
      "description": "string",
      "preview_image_urls": []
    }
  ],
  "page": {
    "has_more": false,
    "next_cursor": "string"
  }
}
```

**Declared statuses:** `200`, `400`, `401`, `403`, `429`, `default`

### POST /v1/photography-styles

Create a Photography Style

Creates a Photography Style asynchronously from exactly three Team Assets. No image is generated, so it costs zero Credits.

**Credit cost:** Free

**Operation ID:** `createPhotographyStyle`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | Yes |  |
| `description` | body | `string` | No |  |
| `asset_ids` | body | `string[]` | Yes |  |

#### Request body example

```json
{
  "name": "Quiet Editorial",
  "description": "Soft daylight and restrained warm neutrals.",
  "asset_ids": [
    "ast_style_one",
    "ast_style_two",
    "ast_style_three"
  ]
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Quiet Editorial","description":"Soft daylight and restrained warm neutrals.","asset_ids":["ast_style_one","ast_style_two","ast_style_three"]}' \
  "https://api.nightjar.so/v1/photography-styles"
```

#### Successful response

**202**: Non-cancelable authoring work accepted.

```json
{
  "object": "operation",
  "id": "string",
  "type": "string",
  "status": "queued",
  "request": {},
  "target": {
    "object": "photography_style",
    "id": "string"
  },
  "result": {
    "object": "photography_style",
    "id": "string"
  },
  "error": {
    "code": null,
    "message": null,
    "retryable": null
  },
  "credits": {
    "reserved": 0,
    "charged": 0
  },
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `202`, `401`, `403`, `409`, `422`, `429`, `503`, `default`

### GET /v1/photography-styles/{photography_style_id}

Retrieve a Photography Style

**Operation ID:** `retrievePhotographyStyle`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `photography_style_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/photography-styles/sty_example"
```

#### Successful response

**200**: One Photography Style.

```json
{
  "object": "photography_style",
  "id": "string",
  "scope": "team",
  "name": "string",
  "source_asset_ids": [
    "string"
  ],
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "description": "string",
  "preview_image_urls": [
    "https://example.com/resource"
  ]
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `429`, `default`

### PATCH /v1/photography-styles/{photography_style_id}

Update a Photography Style

Metadata updates and source replacement are free because no image is generated.

**Credit cost:** Free

**Operation ID:** `updatePhotographyStyle`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `photography_style_id` | path | `string` | Yes |  |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | No |  |
| `description` | body | `string` | No |  |
| `asset_ids` | body | `string[]` | No |  |

#### Request body example

```json
{
  "name": "Quiet Editorial · Spring"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request PATCH \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Quiet Editorial · Spring"}' \
  "https://api.nightjar.so/v1/photography-styles/sty_example"
```

#### Successful response

**200**: Returned only for a metadata-only patch, which commits synchronously and free.

```json
{
  "object": "photography_style",
  "id": "string",
  "scope": "team",
  "name": "string",
  "source_asset_ids": [
    "string"
  ],
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "description": "string",
  "preview_image_urls": [
    "https://example.com/resource"
  ]
}
```

**Declared statuses:** `200`, `202`, `401`, `403`, `404`, `409`, `422`, `429`, `503`, `default`

### DELETE /v1/photography-styles/{photography_style_id}

Delete a Photography Style

**Operation ID:** `deletePhotographyStyle`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `photography_style_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request DELETE \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/photography-styles/sty_example"
```

#### Successful response

**204**: Permanently removed from API and UI use.

No response body.

**Declared statuses:** `204`, `401`, `403`, `404`, `409`, `429`, `503`, `default`

## Backgrounds

Reusable background ingredients.

### GET /v1/backgrounds

List Backgrounds

Returns every usable Team-owned and global resource by default, ordered together by created_at desc, id desc. Use scope to narrow the collection.

**Operation ID:** `listBackgrounds`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No |  |
| `after` | query | `string` | No |  |
| `scope` | query | `all \| team \| global` | No | Omit for every resource the Team can use, or filter to Team-owned or global premade resources. |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/backgrounds"
```

#### Successful response

**200**: Team and premade Backgrounds.

```json
{
  "data": [
    {
      "object": "background",
      "id": null,
      "scope": null,
      "name": null,
      "source_asset_ids": null,
      "created_at": null,
      "updated_at": null,
      "description": "string",
      "kind": "backdrop",
      "card_image_url": "https://example.com/resource"
    }
  ],
  "page": {
    "has_more": false,
    "next_cursor": "string"
  }
}
```

**Declared statuses:** `200`, `400`, `401`, `403`, `429`, `default`

### POST /v1/backgrounds

Create a Background

Creates a Background from one Team Asset. It is free when the scene is already clean and costs exactly one Credit only when foreground-removal image generation runs and commits.

**Credit cost:** 0 or 1 Credit

**Operation ID:** `createBackground`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | Yes |  |
| `kind` | body | `backdrop \| location` | Yes |  |
| `asset_id` | body | `string` | Yes |  |

#### Request body example

```json
{
  "name": "Warm Stone Studio",
  "kind": "backdrop",
  "asset_id": "ast_background_source"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Warm Stone Studio","kind":"backdrop","asset_id":"ast_background_source"}' \
  "https://api.nightjar.so/v1/backgrounds"
```

#### Successful response

**202**: Non-cancelable authoring work accepted.

```json
{
  "object": "operation",
  "id": "string",
  "type": "string",
  "status": "queued",
  "request": {},
  "target": {
    "object": "photography_style",
    "id": "string"
  },
  "result": {
    "object": "photography_style",
    "id": "string"
  },
  "error": {
    "code": null,
    "message": null,
    "retryable": null
  },
  "credits": {
    "reserved": 0,
    "charged": 0
  },
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `202`, `401`, `402`, `403`, `409`, `422`, `429`, `503`, `default`

### GET /v1/backgrounds/{background_id}

Retrieve a Background

**Operation ID:** `retrieveBackground`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `background_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/backgrounds/bkg_example"
```

#### Successful response

**200**: One Background.

```json
{
  "object": "background",
  "id": "string",
  "scope": "team",
  "name": "string",
  "source_asset_ids": [
    "string"
  ],
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "description": "string",
  "kind": "backdrop",
  "card_image_url": "https://example.com/resource"
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `429`, `default`

### PATCH /v1/backgrounds/{background_id}

Update a Background

Metadata updates are free. Replacing the source costs one Credit only when foreground-removal image generation runs and commits.

**Credit cost:** 0 or 1 Credit

**Operation ID:** `updateBackground`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `background_id` | path | `string` | Yes |  |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | No |  |
| `kind` | body | `backdrop \| location` | No |  |
| `asset_id` | body | `string` | No |  |

#### Request body example

```json
{
  "name": "Warm Stone Studio · Clean"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request PATCH \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Warm Stone Studio · Clean"}' \
  "https://api.nightjar.so/v1/backgrounds/bkg_example"
```

#### Successful response

**200**: Returned only for a metadata-only patch, which commits synchronously and free.

```json
{
  "object": "background",
  "id": "string",
  "scope": "team",
  "name": "string",
  "source_asset_ids": [
    "string"
  ],
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "description": "string",
  "kind": "backdrop",
  "card_image_url": "https://example.com/resource"
}
```

**Declared statuses:** `200`, `202`, `401`, `402`, `403`, `404`, `409`, `422`, `429`, `503`, `default`

### DELETE /v1/backgrounds/{background_id}

Delete a Background

**Operation ID:** `deleteBackground`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `background_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request DELETE \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/backgrounds/bkg_example"
```

#### Successful response

**204**: Permanently removed from API and UI use.

No response body.

**Declared statuses:** `204`, `401`, `403`, `404`, `409`, `429`, `503`, `default`

## Poses

Reusable pose ingredients.

### GET /v1/poses

List Poses

Returns every usable Team-owned and global resource by default, ordered together by created_at desc, id desc. Use scope to narrow the collection.

**Operation ID:** `listPoses`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No |  |
| `after` | query | `string` | No |  |
| `scope` | query | `all \| team \| global` | No | Omit for every resource the Team can use, or filter to Team-owned or global premade resources. |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/poses"
```

#### Successful response

**200**: Team and premade Poses.

```json
{
  "data": [
    {
      "object": "pose",
      "id": null,
      "scope": null,
      "name": null,
      "source_asset_ids": null,
      "created_at": null,
      "updated_at": null,
      "description": "string",
      "card_image_url": "https://example.com/resource",
      "supported_camera_distances": []
    }
  ],
  "page": {
    "has_more": false,
    "next_cursor": "string"
  }
}
```

**Declared statuses:** `200`, `400`, `401`, `403`, `429`, `default`

### POST /v1/poses

Create a Pose

Creates a Pose from one Team Asset. A new neutral card image is generated, so a successful commit costs exactly one Credit.

**Credit cost:** 1 Credit

**Operation ID:** `createPose`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | Yes |  |
| `asset_id` | body | `string` | Yes |  |
| `supported_camera_distances` | body | `close \| medium \| far[]` | No |  |

#### Request body example

```json
{
  "name": "Relaxed Standing",
  "asset_id": "ast_pose_source"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Relaxed Standing","asset_id":"ast_pose_source"}' \
  "https://api.nightjar.so/v1/poses"
```

#### Successful response

**202**: Non-cancelable authoring work accepted.

```json
{
  "object": "operation",
  "id": "string",
  "type": "string",
  "status": "queued",
  "request": {},
  "target": {
    "object": "photography_style",
    "id": "string"
  },
  "result": {
    "object": "photography_style",
    "id": "string"
  },
  "error": {
    "code": null,
    "message": null,
    "retryable": null
  },
  "credits": {
    "reserved": 0,
    "charged": 0
  },
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `202`, `401`, `402`, `403`, `409`, `422`, `429`, `503`, `default`

### GET /v1/poses/{pose_id}

Retrieve a Pose

**Operation ID:** `retrievePose`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `pose_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/poses/pos_example"
```

#### Successful response

**200**: One Pose.

```json
{
  "object": "pose",
  "id": "string",
  "scope": "team",
  "name": "string",
  "source_asset_ids": [
    "string"
  ],
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "description": "string",
  "card_image_url": "https://example.com/resource",
  "supported_camera_distances": [
    "close"
  ]
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `429`, `default`

### PATCH /v1/poses/{pose_id}

Update a Pose

Metadata updates are free. Replacing the source generates a new card and costs one Credit on successful commit.

**Credit cost:** 0 or 1 Credit

**Operation ID:** `updatePose`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `pose_id` | path | `string` | Yes |  |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | No |  |
| `asset_id` | body | `string` | No |  |
| `supported_camera_distances` | body | `close \| medium \| far[]` | No |  |

#### Request body example

```json
{
  "name": "Relaxed Standing · Front"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request PATCH \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Relaxed Standing · Front"}' \
  "https://api.nightjar.so/v1/poses/pos_example"
```

#### Successful response

**200**: Returned only for a metadata-only patch, which commits synchronously and free.

```json
{
  "object": "pose",
  "id": "string",
  "scope": "team",
  "name": "string",
  "source_asset_ids": [
    "string"
  ],
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "description": "string",
  "card_image_url": "https://example.com/resource",
  "supported_camera_distances": [
    "close"
  ]
}
```

**Declared statuses:** `200`, `202`, `401`, `402`, `403`, `404`, `409`, `422`, `429`, `503`, `default`

### DELETE /v1/poses/{pose_id}

Delete a Pose

**Operation ID:** `deletePose`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `pose_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request DELETE \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/poses/pos_example"
```

#### Successful response

**204**: Permanently removed from API and UI use.

No response body.

**Declared statuses:** `204`, `401`, `403`, `404`, `409`, `429`, `503`, `default`

## Fashion Models

Reusable fashion model ingredients.

### GET /v1/fashion-models

List Fashion Models

Returns every usable Team-owned and global resource by default, ordered together by created_at desc, id desc. Use scope to narrow the collection.

**Operation ID:** `listFashionModels`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No |  |
| `after` | query | `string` | No |  |
| `scope` | query | `all \| team \| global` | No | Omit for every resource the Team can use, or filter to Team-owned or global premade resources. |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/fashion-models"
```

#### Successful response

**200**: Team and premade Fashion Models.

```json
{
  "data": [
    {
      "object": "fashion_model",
      "id": null,
      "scope": null,
      "name": null,
      "source_asset_ids": null,
      "created_at": null,
      "updated_at": null,
      "age_range": null,
      "gender": null,
      "card_image_url": "https://example.com/resource"
    }
  ],
  "page": {
    "has_more": false,
    "next_cursor": "string"
  }
}
```

**Declared statuses:** `200`, `400`, `401`, `403`, `429`, `default`

### POST /v1/fashion-models

Create a Fashion Model

Creates a Fashion Model from one to five Team Assets and required metadata. A new identity card is generated, so a successful commit costs exactly one Credit.

**Credit cost:** 1 Credit

**Operation ID:** `createFashionModel`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | Yes |  |
| `age_range` | body | `18-25 \| 25-35 \| 35-45 \| 45-55 \| 55+` | Yes |  |
| `gender` | body | `male \| female \| neutral` | Yes |  |
| `asset_ids` | body | `string[]` | Yes |  |

#### Request body example

```json
{
  "name": "Alex",
  "age_range": "25-35",
  "gender": "female",
  "asset_ids": [
    "ast_model_front",
    "ast_model_profile"
  ]
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Alex","age_range":"25-35","gender":"female","asset_ids":["ast_model_front","ast_model_profile"]}' \
  "https://api.nightjar.so/v1/fashion-models"
```

#### Successful response

**202**: Non-cancelable authoring work accepted.

```json
{
  "object": "operation",
  "id": "string",
  "type": "string",
  "status": "queued",
  "request": {},
  "target": {
    "object": "photography_style",
    "id": "string"
  },
  "result": {
    "object": "photography_style",
    "id": "string"
  },
  "error": {
    "code": null,
    "message": null,
    "retryable": null
  },
  "credits": {
    "reserved": 0,
    "charged": 0
  },
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `202`, `401`, `402`, `403`, `409`, `422`, `429`, `503`, `default`

### GET /v1/fashion-models/{fashion_model_id}

Retrieve a Fashion Model

**Operation ID:** `retrieveFashionModel`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `fashion_model_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/fashion-models/mdl_example"
```

#### Successful response

**200**: One Fashion Model.

```json
{
  "object": "fashion_model",
  "id": "string",
  "scope": "team",
  "name": "string",
  "source_asset_ids": [
    "string"
  ],
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "age_range": "18-25",
  "gender": "male",
  "card_image_url": "https://example.com/resource"
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `429`, `default`

### PATCH /v1/fashion-models/{fashion_model_id}

Update a Fashion Model

Metadata updates are free. Replacing source Assets generates a new identity card and costs one Credit on successful commit.

**Credit cost:** 0 or 1 Credit

**Operation ID:** `updateFashionModel`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `fashion_model_id` | path | `string` | Yes |  |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `name` | body | `string` | No |  |
| `age_range` | body | `18-25 \| 25-35 \| 35-45 \| 45-55 \| 55+` | No |  |
| `gender` | body | `male \| female \| neutral` | No |  |
| `asset_ids` | body | `string[]` | No |  |

#### Request body example

```json
{
  "name": "Alex · Summer"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request PATCH \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"name":"Alex · Summer"}' \
  "https://api.nightjar.so/v1/fashion-models/mdl_example"
```

#### Successful response

**200**: Returned only for a metadata-only patch, which commits synchronously and free.

```json
{
  "object": "fashion_model",
  "id": "string",
  "scope": "team",
  "name": "string",
  "source_asset_ids": [
    "string"
  ],
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "age_range": "18-25",
  "gender": "male",
  "card_image_url": "https://example.com/resource"
}
```

**Declared statuses:** `200`, `202`, `401`, `402`, `403`, `404`, `409`, `422`, `429`, `503`, `default`

### DELETE /v1/fashion-models/{fashion_model_id}

Delete a Fashion Model

**Operation ID:** `deleteFashionModel`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `fashion_model_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request DELETE \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/fashion-models/mdl_example"
```

#### Successful response

**204**: Permanently removed from API and UI use.

No response body.

**Declared statuses:** `204`, `401`, `403`, `404`, `409`, `429`, `503`, `default`

## Operations

Asynchronous reusable-ingredient authoring work.

### GET /v1/operations

List authoring Operations

Returns Team authoring Operations ordered by created_at desc, id desc. Use the filters to recover specific in-flight or historical work.

**Operation ID:** `listOperations`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No |  |
| `after` | query | `string` | No |  |
| `status` | query | `queued \| processing \| completed \| failed` | No |  |
| `type` | query | `photography_style.create \| photography_style.update \| background.create \| background.update \| pose.create \| pose.update \| fashion_model.create \| fashion_model.update` | No |  |
| `target_id` | query | `string` | No |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/operations"
```

#### Successful response

**200**: Matching authoring Operations.

```json
{
  "data": [
    {
      "object": "operation",
      "id": "string",
      "type": "string",
      "status": "queued",
      "request": {},
      "target": null,
      "result": null,
      "error": null,
      "credits": {
        "reserved": null,
        "charged": null
      },
      "created_at": "2026-08-16T10:00:00.000Z",
      "updated_at": "2026-08-16T10:00:00.000Z",
      "started_at": "2026-08-16T10:00:00.000Z",
      "finished_at": "2026-08-16T10:00:00.000Z"
    }
  ],
  "page": {
    "has_more": false,
    "next_cursor": "string"
  }
}
```

**Declared statuses:** `200`, `400`, `401`, `403`, `429`, `default`

### GET /v1/operations/{operation_id}

Retrieve an authoring Operation

**Operation ID:** `retrieveOperation`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `operation_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/operations/op_example"
```

#### Successful response

**200**: Authoritative authoring lifecycle snapshot.

```json
{
  "object": "operation",
  "id": "string",
  "type": "string",
  "status": "queued",
  "request": {},
  "target": {
    "object": "photography_style",
    "id": "string"
  },
  "result": {
    "object": "photography_style",
    "id": "string"
  },
  "error": {
    "code": null,
    "message": null,
    "retryable": null
  },
  "credits": {
    "reserved": 0,
    "charged": 0
  },
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z"
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `429`, `default`

## Generations

Asynchronous creative workflow executions.

### GET /v1/generations

List Generations

**Operation ID:** `listGenerations`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `limit` | query | `integer` | No |  |
| `after` | query | `string` | No |  |
| `workflow` | query | `product_photography \| edit_images \| upscale` | No |  |
| `status` | query | `queued \| processing \| completed \| failed` | No |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/generations"
```

#### Successful response

**200**: Public-API Generations for the Team across all of its API Credentials, ordered by created_at desc, id desc. Web-only Generations that cannot satisfy this contract are excluded.

```json
{
  "data": [
    {}
  ],
  "page": {
    "has_more": false,
    "next_cursor": "string"
  }
}
```

**Declared statuses:** `200`, `400`, `401`, `403`, `429`, `default`

### GET /v1/generations/{generation_id}

Retrieve a Generation

**Operation ID:** `retrieveGeneration`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `generation_id` | path | `string` | Yes |  |

#### Request example

```bash
curl --fail-with-body \
  --request GET \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  "https://api.nightjar.so/v1/generations/gen_example"
```

#### Successful response

**200**: Authoritative Generation lifecycle and output snapshot.

```json
{
  "object": "generation",
  "id": "string",
  "initiated_by": null,
  "status": null,
  "output_summary": null,
  "outputs": [],
  "credits": null,
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z",
  "workflow": "product_photography",
  "request": null
}
```

**Declared statuses:** `200`, `401`, `403`, `404`, `429`, `default`

### POST /v1/generations/product-photography

Start Product Photography or a Photoshoot

Every selected Product and loose Asset is a joint subject in every output. single_shots costs one Credit per requested 1k or 2k image and two Credits per requested 4k image. photoshoot costs two Credits total and produces four output slots. The complete amount is planned and reserved before admission.

**Credit cost:** 1 Credit per generated image

**Operation ID:** `createProductPhotographyGeneration`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |

#### Request body example

```json
{
  "product_ids": [
    "prd_01JPRODUCT"
  ],
  "asset_ids": [
    "ast_01JLOOSEANGLE"
  ],
  "photography_style_id": "sty_01JEDITORIAL",
  "background": {
    "type": "background",
    "background_id": "bkg_01JWARMSTUDIO"
  },
  "fashion_model": {
    "type": "selected",
    "fashion_model_id": "mdl_01JALEX"
  },
  "pose_id": "pos_01JSTANDING",
  "camera_distance": "medium",
  "custom_directions": "Keep both products clearly visible.",
  "output_mode": "single_shots",
  "image_count": 2,
  "aspect_ratio": "4:5",
  "resolution": "2k",
  "output_format": "webp"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"product_ids":["prd_01JPRODUCT"],"asset_ids":["ast_01JLOOSEANGLE"],"photography_style_id":"sty_01JEDITORIAL","background":{"type":"background","background_id":"bkg_01JWARMSTUDIO"},"fashion_model":{"type":"selected","fashion_model_id":"mdl_01JALEX"},"pose_id":"pos_01JSTANDING","camera_distance":"medium","custom_directions":"Keep both products clearly visible.","output_mode":"single_shots","image_count":2,"aspect_ratio":"4:5","resolution":"2k","output_format":"webp"}' \
  "https://api.nightjar.so/v1/generations/product-photography"
```

#### Successful response

**202**: Non-cancelable Generation admitted with stable output slots.

```json
{
  "object": "generation",
  "id": "string",
  "initiated_by": null,
  "status": null,
  "output_summary": null,
  "outputs": [],
  "credits": null,
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z",
  "workflow": "product_photography",
  "request": null
}
```

**Declared statuses:** `202`, `401`, `402`, `403`, `409`, `422`, `429`, `503`, `default`

### POST /v1/generations/edit-images

Start Edit Images

All one to eight input Assets jointly guide exactly one edited output. Cost is one Credit at 1k or 2k and two Credits at 4k, planned and reserved before admission.

**Credit cost:** 1 or 2 Credits

**Operation ID:** `createEditImagesGeneration`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `aspect_ratio` | body | `21:9 \| 1:1 \| 4:3 \| 3:2 \| 2:3 \| 5:4 \| 4:5 \| 3:4 \| 16:9 \| 9:16` | Yes |  |
| `resolution` | body | `1k \| 2k \| 4k` | Yes |  |
| `output_format` | body | `jpeg \| png \| webp` | Yes |  |
| `asset_ids` | body | `string[]` | Yes |  |
| `instructions` | body | `string` | Yes |  |

#### Request body example

```json
{
  "asset_ids": [
    "ast_01JPRODUCT",
    "ast_01JSCENE"
  ],
  "instructions": "Place the product from image 1 into the scene from image 2.",
  "aspect_ratio": "4:5",
  "resolution": "2k",
  "output_format": "webp"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"asset_ids":["ast_01JPRODUCT","ast_01JSCENE"],"instructions":"Place the product from image 1 into the scene from image 2.","aspect_ratio":"4:5","resolution":"2k","output_format":"webp"}' \
  "https://api.nightjar.so/v1/generations/edit-images"
```

#### Successful response

**202**: Non-cancelable Generation admitted with stable output slots.

```json
{
  "object": "generation",
  "id": "string",
  "initiated_by": null,
  "status": null,
  "output_summary": null,
  "outputs": [],
  "credits": null,
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z",
  "workflow": "product_photography",
  "request": null
}
```

**Declared statuses:** `202`, `401`, `402`, `403`, `409`, `422`, `429`, `503`, `default`

### POST /v1/generations/upscale

Start Upscale

Produces exactly one upscaled output. Cost is one Credit at 2k and two Credits at 4k, planned and reserved before admission. An Asset already at or above the requested target is rejected synchronously and costs nothing.

**Credit cost:** 1 or 2 Credits

**Operation ID:** `createUpscaleGeneration`

#### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `Idempotency-Key` | header | `string` | Yes | Scoped by API Credential, HTTP method, and the concrete canonical path including resolved path parameters, and retained for at least 24 hours. The fingerprint uses normalized request intent rather than raw JSON. Same-key/same-intent replays the original admission status, headers, and body exactly; an in-flight duplicate returns 409 and mismatched reuse returns 422. |
| `asset_id` | body | `string` | Yes |  |
| `target_resolution` | body | `2k \| 4k` | Yes |  |
| `output_format` | body | `jpeg \| png \| webp` | Yes |  |

#### Request body example

```json
{
  "asset_id": "ast_01JPRODUCT",
  "target_resolution": "2k",
  "output_format": "webp"
}
```

#### Request example

```bash
curl --fail-with-body \
  --request POST \
  --header "Authorization: Bearer ${NIGHTJAR_API_KEY}" \
  --header "Content-Type: application/json" \
  --data '{"asset_id":"ast_01JPRODUCT","target_resolution":"2k","output_format":"webp"}' \
  "https://api.nightjar.so/v1/generations/upscale"
```

#### Successful response

**202**: Non-cancelable Generation admitted with stable output slots.

```json
{
  "object": "generation",
  "id": "string",
  "initiated_by": null,
  "status": null,
  "output_summary": null,
  "outputs": [],
  "credits": null,
  "created_at": "2026-08-16T10:00:00.000Z",
  "updated_at": "2026-08-16T10:00:00.000Z",
  "started_at": "2026-08-16T10:00:00.000Z",
  "finished_at": "2026-08-16T10:00:00.000Z",
  "workflow": "product_photography",
  "request": null
}
```

**Declared statuses:** `202`, `401`, `402`, `403`, `409`, `422`, `429`, `503`, `default`
