# Layouts

Layouts, which candidates fill in to make proposals

Base URL: `https://api.cv-transformer.com/v1`. Endpoints: `GET /layouts`, `POST /layouts`, `DELETE /layouts`, `PATCH /layouts`. Authenticate with the header `Authorization: Bearer YOUR_API_KEY` (see [authentication](https://www.cv-transformer.com/en/api-reference/authentication.md)).

## The layout object

| Attribute | Type | Description |
| --- | --- | --- |
| `id` | `uuid` | Unique identifier. |
| `creator_id` | `uuid \| null` | Identifier of the user that created this layout. Set automatically: the signed-in user, or the user who connected the OAuth app. Null for layouts created with an API key. Read-only. References [`user_profiles.user_id`](https://www.cv-transformer.com/en/api-reference/user_profiles.md). |
| `organization_id` | `uuid` | Identifier of the related organization. References [`organizations.id`](https://www.cv-transformer.com/en/api-reference/organizations.md). |
| `parent_id` | `uuid \| null` | Identifier of the layout this layout was copied from. References [`layouts.id`](https://www.cv-transformer.com/en/api-reference/layouts.md). |
| `created_at` | `timestamp with time zone` | Creation timestamp. Read-only. |
| `updated_at` | `timestamp with time zone` | Last modification timestamp. |
| `archived_at` | `timestamp with time zone \| null` | Archival timestamp. |
| `assets` | `Array<LayoutAsset>` | Reusable assets used in the layout. |
| `custom_css` | `text` | Custom CSS applied to the layout. |
| `default_values` | `Partial<Record<Language, JsonObject>>` | Default values of the layout's variables, per language. When empty, they are generated. |
| `description` | `text` | Description of the layout, shown in the layout library. |
| `export_filename` | `text` | Filename of exported PDF files (without .pdf). Can contain {{variables}}. |
| `featured` | `boolean` | Whether the layout is featured in the layout library. Read-only. |
| `fts` | `tsvector` | Full text search index. Read-only. |
| `languages` | `[Language, ...Array<Language>]` | Languages the layout is translated into. The first is the default. |
| `name` | `text` | Name of the layout. |
| `page_height` | `integer` | Page height, in pixels. |
| `page_width` | `integer` | Page width, in pixels. |
| `secret` | `uuid` | Secret string. Required to view the layout when its `visibility` is `private`. Read-only. |
| `sections` | `Array<LayoutSection>` | Sections the layout consists of. |
| `share_instructions` | `text` | Instructions shown in the share dialog, if any. |
| `slug` | `text` | Unique string to look up the layout in the layout library. |
| `thumbnail` | `text \| null` | Thumbnail image of the layout. Generated automatically. |
| `tokens` | `Array<LayoutDesignToken>` | Design tokens used in the layout. |
| `variables` | `Array<LayoutVariable>` | Variables used in the layout. Each candidate using the layout fills them in. |
| `visibility` | `LayoutVisibility` | Visibility of the layout. `public` layouts are published to the layout library. |
| `web_settings` | `LayoutWebSettings \| null` | Settings for sharing the layout's proposals on the web. |

```json
{
  "id": "00000000-0000-0000-0000-000000000000",
  "creator_id": "00000000-0000-0000-0000-000000000000",
  "organization_id": "00000000-0000-0000-0000-000000000000",
  "parent_id": "00000000-0000-0000-0000-000000000000",
  "created_at": "2026-01-01T12:00:00+00:00",
  "updated_at": "2026-01-01T12:00:00+00:00",
  "archived_at": null,
  "assets": [
    {
      "family": "Roboto",
      "type": "font",
      "url": "https://ddcaybdkekwxbpgpotoa.supabase.co/storage/v1/object/public/common/organizations/00000000-0000-0000-0000-000000000000/roboto.ttf"
    },
    {
      "label": "Example",
      "type": "image",
      "url": "https://ddcaybdkekwxbpgpotoa.supabase.co/storage/v1/object/public/common/organizations/00000000-0000-0000-0000-000000000000/example.png"
    }
  ],
  "custom_css": "",
  "default_values": {
    "en": {
      "message": "Hello"
    },
    "nl": {
      "message": "Hallo"
    }
  },
  "description": "",
  "export_filename": "",
  "featured": false,
  "fts": "'doe':2 'john':1",
  "languages": [
    "en",
    "cs",
    "da-DK",
    "de",
    "en-GB",
    "es",
    "fr",
    "ja-JP",
    "nb-NO",
    "nl",
    "pt-BR",
    "sv-SE",
    "yue",
    "zh"
  ],
  "name": "",
  "page_height": 0,
  "page_width": 0,
  "secret": "00000000-0000-0000-0000-000000000000",
  "sections": [
    {
      "body": {
        "align": "left",
        "background": "#FFFFFF",
        "bold": false,
        "border": null,
        "borderRadius": "0",
        "color": "#000000",
        "columns": 1,
        "font": {
          "family": "Roboto",
          "letter_spacing": 0,
          "line_height": 1.5,
          "paragraph_spacing": 0.5,
          "size": 16
        },
        "grow": true,
        "id": "text",
        "italic": false,
        "margin": "0",
        "padding": 16,
        "listStyle": "disc",
        "split": false,
        "strikethrough": false,
        "textTransform": "none",
        "transform": "",
        "underline": false,
        "value": {
          "default": "Hello",
          "nl": "Hallo"
        },
        "visibility": true,
        "type": "text"
      },
      "footer": null,
      "header": null,
      "id": "section",
      "label": "Section",
      "type": "multi-page"
    }
  ],
  "share_instructions": "",
  "slug": "",
  "thumbnail": "",
  "tokens": [
    {
      "id": "primary-color",
      "name": "Primary color",
      "type": "color",
      "value": "#000000"
    },
    {
      "id": "font-size",
      "name": "Font size",
      "type": "length",
      "value": 16
    }
  ],
  "variables": [
    {
      "ai_instructions": "",
      "sensitive": false,
      "default": "",
      "key": "name",
      "max_length": null,
      "min_length": 0,
      "type": "inline-text"
    },
    {
      "ai_instructions": "",
      "sensitive": false,
      "default": "",
      "key": "description",
      "max_length": null,
      "min_length": 0,
      "type": "text-block"
    }
  ],
  "visibility": "public",
  "web_settings": {
    "color_accent": "#000000",
    "color_background": "#FFFFFF",
    "color_text": "#000000"
  }
}
```

### Types

```typescript
type LayoutWebSettings = {
  color_accent: string;
  color_background: string;
  color_text: string;
};

type LayoutVisibility = "private" | "public";

type LayoutVariable = 
  | LayoutDateRangeVariable
  | LayoutDateVariable
  | LayoutEnumVariable
  | LayoutImageVariable
  | LayoutInlineTextVariable
  | LayoutJsonVariable
  | LayoutListVariable
  | LayoutNumberVariable
  | LayoutObjectVariable
  | LayoutRatingVariable
  | LayoutTextBlockVariable;

type LayoutDateRangeVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default: {
    end: string;
    start: string;
  };
  precision: "year" | "month" | "day";
  type: "date-range";
};

type LayoutDateVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default: string;
  precision: "year" | "month" | "day";
  type: "date";
};

type LayoutEnumVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default: string;
  options: Array<MultiLanguageString>;
  type: "enum";
};

type LayoutImageVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  type: "image";
};

type LayoutInlineTextVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default: string;
  max_length: number | null;
  min_length: number;
  type: "inline-text";
};

type LayoutJsonVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default: Json;
  type: "json";
};

type LayoutListVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default?: string | null;
  items: Array<LayoutVariable>;
  max_length: number | null;
  min_length: number;
  type: "list";
};

type LayoutNumberVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default: number | null;
  max: number | null;
  min: number | null;
  type: "number";
};

type LayoutObjectVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  fields: Array<LayoutVariable>;
  type: "object";
};

type LayoutRatingVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default: number;
  type: "rating";
};

type LayoutTextBlockVariable = {
  ai_instructions: false | string;
  key: string;
  sensitive: boolean;
  static?: true;
  default: string;
  max_length: number | null;
  min_length: number;
  type: "text-block";
};

type Json = 
  | string
  | number
  | boolean
  | null
  | Array<Json>
  | Record<string, Json>;

type MultiLanguageString = {
  default: string;
  [x: string]: string;
};

type LayoutDesignToken = LayoutColorToken | LayoutFontToken | LayoutLengthToken;

type LayoutColorToken = {
  id: string;
  name: string;
  type: "color";
  value: string;
};

type LayoutFontToken = {
  id: string;
  name: string;
  type: "font";
  value: LayoutFont;
};

type LayoutLengthToken = {
  id: string;
  name: string;
  type: "length";
  value: number;
};

type LayoutFont = {
  family: string | null;
  letter_spacing: number;
  line_height: number;
  paragraph_spacing: number;
  size: number;
};

type LayoutSection = 
  | {
      footer: LayoutNodeDefinition | null;
      header: LayoutNodeDefinition | null;
      id: string;
      label: string;
      candidate_attachments?: boolean;
      static_attachments?: Array<CandidateAttachment>;
      type: "attachment";
    }
  | {
      footer: LayoutNodeDefinition | null;
      header: LayoutNodeDefinition | null;
      id: string;
      label: string;
      body: LayoutNodeDefinition;
      type: "multi-page";
    }
  | {
      id: string;
      label: string;
      node: LayoutNodeDefinition;
      type: "single-page";
    };

type CandidateAttachment = {
  detected_source_language: string | null;
  external_id: string | number | null;
  label?: string;
  markings?: Array<Array<AttachmentMarking>>;
  text: MultiLanguageString;
  type: "image" | "image-pdf" | "link" | "pdf" | "text";
  url: string;
};

type LayoutNodeDefinition = 
  | LayoutConcatenationNodeDefinition
  | LayoutImageNodeDefinition
  | LayoutListNodeDefinition
  | LayoutRatingNodeDefinition
  | LayoutRowNodeDefinition
  | LayoutStackNodeDefinition
  | LayoutTextNodeDefinition
  | LayoutWrappingRowNodeDefinition;

type LayoutConcatenationNodeDefinition = {
  background: string;
  border: LayoutNodeBorder | null;
  borderRadius: string | number;
  grow: boolean;
  href?: string;
  id: string;
  label?: string;
  margin: string | number;
  padding: string | number;
  split: boolean | "always";
  transform: string;
  visibility: LayoutBooleanExpression;
  align: "left" | "center" | "right" | "justify";
  bold: boolean;
  color: string;
  columns: number;
  font: LayoutFont | string;
  italic: boolean;
  listStyle: string | Array<string>;
  strikethrough: boolean;
  textTransform: "none" | "uppercase" | "lowercase" | "capitalize";
  type: "concatenation";
  underline: boolean;
  value: MultiLanguageString;
  prefix: MultiLanguageString;
  selector: string;
  separator: MultiLanguageString;
  suffix: MultiLanguageString;
};

type LayoutImageNodeDefinition = {
  background: string;
  border: LayoutNodeBorder | null;
  borderRadius: string | number;
  grow: boolean;
  href?: string;
  id: string;
  label?: string;
  margin: string | number;
  padding: string | number;
  split: boolean | "always";
  transform: string;
  visibility: LayoutBooleanExpression;
  aspect: "1/1" | "4/3" | "16/9" | "2/1" | "3/4" | "9/16" | "1/2" | null;
  fit: "cover" | "contain";
  src: string;
  type: "image";
};

type LayoutListNodeDefinition = {
  background: string;
  border: LayoutNodeBorder | null;
  borderRadius: string | number;
  grow: boolean;
  href?: string;
  id: string;
  label?: string;
  margin: string | number;
  padding: string | number;
  split: boolean | "always";
  transform: string;
  visibility: LayoutBooleanExpression;
  align: "stretch" | "start" | "center" | "end";
  divider: LayoutNodeBorder | null;
  gap: string | number;
  item: LayoutNodeDefinition;
  justify: "start" | "center" | "end";
  orientation: "horizontal" | "vertical";
  selector: string;
  type: "list";
};

type LayoutRatingNodeDefinition = {
  background: string;
  border: LayoutNodeBorder | null;
  borderRadius: string | number;
  grow: boolean;
  href?: string;
  id: string;
  label?: string;
  margin: string | number;
  padding: string | number;
  split: boolean | "always";
  transform: string;
  visibility: LayoutBooleanExpression;
  color: string;
  selector: string;
  size: string | number;
  style: string;
  type: "rating";
};

type LayoutRowNodeDefinition = {
  background: string;
  border: LayoutNodeBorder | null;
  borderRadius: string | number;
  grow: boolean;
  href?: string;
  id: string;
  label?: string;
  margin: string | number;
  padding: string | number;
  split: boolean | "always";
  transform: string;
  visibility: LayoutBooleanExpression;
  align: "stretch" | "start" | "center" | "end";
  divider: LayoutNodeBorder | null;
  gap: string | number;
  columns: Array<{
    node: LayoutNodeDefinition;
    width: number;
  }>;
  type: "row";
};

type LayoutStackNodeDefinition = {
  background: string;
  border: LayoutNodeBorder | null;
  borderRadius: string | number;
  grow: boolean;
  href?: string;
  id: string;
  label?: string;
  margin: string | number;
  padding: string | number;
  split: boolean | "always";
  transform: string;
  visibility: LayoutBooleanExpression;
  align: "stretch" | "start" | "center" | "end";
  divider: LayoutNodeBorder | null;
  gap: string | number;
  children: Array<LayoutNodeDefinition>;
  justify: "start" | "center" | "end";
  type: "stack";
};

type LayoutTextNodeDefinition = {
  background: string;
  border: LayoutNodeBorder | null;
  borderRadius: string | number;
  grow: boolean;
  href?: string;
  id: string;
  label?: string;
  margin: string | number;
  padding: string | number;
  split: boolean | "always";
  transform: string;
  visibility: LayoutBooleanExpression;
  align: "left" | "center" | "right" | "justify";
  bold: boolean;
  color: string;
  columns: number;
  font: LayoutFont | string;
  italic: boolean;
  listStyle: string | Array<string>;
  strikethrough: boolean;
  textTransform: "none" | "uppercase" | "lowercase" | "capitalize";
  type: "text";
  underline: boolean;
  value: MultiLanguageString;
};

type LayoutWrappingRowNodeDefinition = {
  background: string;
  border: LayoutNodeBorder | null;
  borderRadius: string | number;
  grow: boolean;
  href?: string;
  id: string;
  label?: string;
  margin: string | number;
  padding: string | number;
  split: boolean | "always";
  transform: string;
  visibility: LayoutBooleanExpression;
  align: "stretch" | "start" | "center" | "end";
  divider: LayoutNodeBorder | null;
  gap: string | number;
  children: Array<LayoutNodeDefinition>;
  justify: "start" | "center" | "end";
  type: "wrapping-row";
};

type LayoutNodeBorder = {
  color: string;
  style: "solid" | "dashed" | "dotted";
  width: string | number;
};

type LayoutBooleanExpression = 
  | boolean
  | "anonymous" | "non-anonymous" | "web" | "pdf"
  | {
      max: number | null;
      min: number | null;
      selector: string;
    }
  | {
      condition: "empty" | "non-empty";
      selector: string;
    }
  | {
      condition: "equal-to" | "not-equal-to" | "greater-than" | "less-than";
      selector: string;
      value: string;
    }
  | {
      condition: "and" | "or";
      expressions: Array<LayoutBooleanExpression>;
    };

type AttachmentMarking = {
  color?: string;
  height: number;
  left: number;
  top: number;
  width: number;
};

type Language = 
  | "en"
  | "cs"
  | "da-DK"
  | "de"
  | "en-GB"
  | "es"
  | "fr"
  | "ja-JP"
  | "nb-NO"
  | "nl"
  | "pt-BR"
  | "sv-SE"
  | "yue"
  | "zh";

type JsonObject = Record<string, Json>;

type LayoutAsset = LayoutFontAsset | LayoutImageAsset;

type LayoutFontAsset = {
  family: string;
  type: "font";
  url: string;
};

type LayoutImageAsset = {
  label: string;
  type: "image";
  url: string;
};
```

### Related resources

Embed related resources with the [`select` parameter](https://www.cv-transformer.com/en/api-reference/selecting.md), for example `select=*,organizations!organization_id(*)`. Resources related through more than one column need the column as a hint, like `table!column(*)`.

| Embed | Type | Description |
| --- | --- | --- |
| `organizations!organization_id(*)` | `object \| null` | The [organization](https://www.cv-transformer.com/en/api-reference/organizations.md) referenced by `organization_id`. |
| `user_profiles(*)` | `object \| null` | The [user profile](https://www.cv-transformer.com/en/api-reference/user_profiles.md) referenced by `creator_id`. |
| `candidates(*)` | `array` | The [candidates](https://www.cv-transformer.com/en/api-reference/candidates.md) referencing this layout through `candidates.layout_id`. |
| `organizations!default_layout_id(*)` | `array` | The [organizations](https://www.cv-transformer.com/en/api-reference/organizations.md) referencing this layout through `organizations.default_layout_id`. |

## List layouts

`GET /v1/layouts` · requires the `layouts:read` scope

Filter on any attribute with `column=operator.value` (see [filtering](https://www.cv-transformer.com/en/api-reference/filtering.md)). Choose columns and embed related resources with `select` (see [selecting](https://www.cv-transformer.com/en/api-reference/selecting.md)), and sort and paginate with `order`, `limit` and `offset` (see [sorting and pagination](https://www.cv-transformer.com/en/api-reference/sorting-pagination.md)).

```bash
curl "https://api.cv-transformer.com/v1/layouts?limit=10&select=*" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Responses:

- `200`: A list of layouts with the selected columns. The `Content-Range` header holds the range returned and the total count, like `0-9/42`.
- `206`: Partial Content
- `400`: The request is malformed, for example an unknown column or an invalid filter.
- `401`: The API key is missing or invalid.
- `403`: The API key doesn't have the scope this request needs.
- `429`: The API key made more requests per minute than its rate limit allows. Retry after the number of seconds in the `Retry-After` header.

## Create layouts

`POST /v1/layouts` · requires the `layouts:write` scope

Insert one layout or an array of layouts. The body is a `Partial` layout object, so columns with a default can be left out. Read-only attributes can't be written.

```bash
curl "https://api.cv-transformer.com/v1/layouts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "organization_id": "00000000-0000-0000-0000-000000000000",
    "assets": [
      {
        "family": "Roboto",
        "type": "font",
        "url": "https://ddcaybdkekwxbpgpotoa.supabase.co/storage/v1/object/public/common/organizations/00000000-0000-0000-0000-000000000000/roboto.ttf"
      },
      {
        "label": "Example",
        "type": "image",
        "url": "https://ddcaybdkekwxbpgpotoa.supabase.co/storage/v1/object/public/common/organizations/00000000-0000-0000-0000-000000000000/example.png"
      }
    ],
    "custom_css": "",
    "default_values": {
      "en": { "message": "Hello" },
      "nl": { "message": "Hallo" }
    },
    "description": "",
    "export_filename": "",
    "languages": [
      "en",
      "cs",
      "da-DK",
      "de",
      "en-GB",
      "es",
      "fr",
      "ja-JP",
      "nb-NO",
      "nl",
      "pt-BR",
      "sv-SE",
      "yue",
      "zh"
    ],
    "name": "",
    "page_height": 0,
    "page_width": 0,
    "sections": [
      {
        "body": {
          "align": "left",
          "background": "#FFFFFF",
          "bold": false,
          "border": null,
          "borderRadius": "0",
          "color": "#000000",
          "columns": 1,
          "font": {
            "family": "Roboto",
            "letter_spacing": 0,
            "line_height": 1.5,
            "paragraph_spacing": 0.5,
            "size": 16
          },
          "grow": true,
          "id": "text",
          "italic": false,
          "margin": "0",
          "padding": 16,
          "listStyle": "disc",
          "split": false,
          "strikethrough": false,
          "textTransform": "none",
          "transform": "",
          "underline": false,
          "value": { "default": "Hello", "nl": "Hallo" },
          "visibility": true,
          "type": "text"
        },
        "footer": null,
        "header": null,
        "id": "section",
        "label": "Section",
        "type": "multi-page"
      }
    ],
    "share_instructions": "",
    "slug": "",
    "tokens": [
      {
        "id": "primary-color",
        "name": "Primary color",
        "type": "color",
        "value": "#000000"
      },
      {
        "id": "font-size",
        "name": "Font size",
        "type": "length",
        "value": 16
      }
    ],
    "variables": [
      {
        "ai_instructions": "",
        "sensitive": false,
        "default": "",
        "key": "name",
        "max_length": null,
        "min_length": 0,
        "type": "inline-text"
      },
      {
        "ai_instructions": "",
        "sensitive": false,
        "default": "",
        "key": "description",
        "max_length": null,
        "min_length": 0,
        "type": "text-block"
      }
    ],
    "visibility": "public"
  }'
```

Responses:

- `201`: Created. With the header `Prefer: return=representation`, the body holds the created layouts with the selected columns.
- `400`: The request is malformed, for example an unknown column, an invalid filter or an invalid body.
- `401`: The API key is missing or invalid.
- `403`: The API key doesn't have the scope this request needs, or no permission for this change, for example when writing rows of another organization.
- `409`: The change conflicts with existing data, for example a duplicate key or a reference to a row that does not exist.
- `429`: The API key made more requests per minute than its rate limit allows. Retry after the number of seconds in the `Retry-After` header.

## Delete layouts

`DELETE /v1/layouts` · requires the `layouts:write` scope

Filter on any attribute with `column=operator.value` (see [filtering](https://www.cv-transformer.com/en/api-reference/filtering.md)). Without filters, this request deletes all layouts the API key has access to. Always filter on at least one column, like `id=eq.{id}`.

```bash
curl -X DELETE "https://api.cv-transformer.com/v1/layouts?id=eq.00000000-0000-0000-0000-000000000000" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Responses:

- `204`: Deleted. With the header `Prefer: return=representation`, the response is a `200` whose body holds the deleted layouts with the selected columns.
- `400`: The request is malformed, for example an unknown column or an invalid filter.
- `401`: The API key is missing or invalid.
- `403`: The API key doesn't have the scope this request needs, or no permission for this change, for example when writing rows of another organization.
- `429`: The API key made more requests per minute than its rate limit allows. Retry after the number of seconds in the `Retry-After` header.

## Update layouts

`PATCH /v1/layouts` · requires the `layouts:write` scope

Filter on any attribute with `column=operator.value` (see [filtering](https://www.cv-transformer.com/en/api-reference/filtering.md)). Without filters, this request updates all layouts the API key has access to. Always filter on at least one column, like `id=eq.{id}`. The body holds the attributes to change.

```bash
curl -X PATCH "https://api.cv-transformer.com/v1/layouts?id=eq.00000000-0000-0000-0000-000000000000" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "assets": [
      {
        "family": "Roboto",
        "type": "font",
        "url": "https://ddcaybdkekwxbpgpotoa.supabase.co/storage/v1/object/public/common/organizations/00000000-0000-0000-0000-000000000000/roboto.ttf"
      },
      {
        "label": "Example",
        "type": "image",
        "url": "https://ddcaybdkekwxbpgpotoa.supabase.co/storage/v1/object/public/common/organizations/00000000-0000-0000-0000-000000000000/example.png"
      }
    ]
  }'
```

Responses:

- `204`: Updated. With the header `Prefer: return=representation`, the response is a `200` whose body holds the updated layouts with the selected columns.
- `400`: The request is malformed, for example an unknown column, an invalid filter or an invalid body.
- `401`: The API key is missing or invalid.
- `403`: The API key doesn't have the scope this request needs, or no permission for this change, for example when writing rows of another organization.
- `409`: The change conflicts with existing data, for example a duplicate key or a reference to a row that does not exist.
- `429`: The API key made more requests per minute than its rate limit allows. Retry after the number of seconds in the `Retry-After` header.

Errors have a JSON body with a `message`, `details` and `hint`, and for database errors a `code`. See the [introduction](https://www.cv-transformer.com/en/api-reference.md) for more on errors. The full API is described by the OpenAPI spec at https://api.cv-transformer.com/v1/openapi.json.
