---
title: "Create a brand template"
method: POST
path: "/brand-templates/create"
tags: ["Brand Templates"]
---

# Create a brand template

`POST /brand-templates/create`

## Request

- Base URL: `https://api.reportingninja.com/v1`
- URL: `https://api.reportingninja.com/v1/brand-templates/create`
- Auth: HTTP bearer

Creates a brand template of the account: the look a report is printed with and a client portal view is drawn with -- the fonts, the colours, the margins, the front cover, the table of contents and the header of the portal.

A NEW TEMPLATE IS BORN COMPLETE. Every setting a body does not carry takes the default of the product, and the template is born with the logo of the platform, which is what makes include_logo true mean something on the first call. A body with a name alone therefore writes a template that prints correctly; send only what is meant to differ.

THE BODY IS THE OBJECT /v1/brand-templates/get ANSWERS. The keys that read publishes and a write has no column for -- brand_template_key, is_system, logo and cover_image -- are accepted and ignored with a READ_ONLY_FIELD_IGNORED warning, and a null means "leave it as it is", which on a creation means the default.

THE TWO IMAGES ARE NOT WRITTEN HERE: /v1/brand-templates/set-logo and /v1/brand-templates/set-cover-image each write one file. A creation with cover_style 'image' is refused with details.reason cover_image_required, because a template being created carries no image yet.

Colours are written as #RRGGBB and stored in lower case. cover_tone auto, web_font_family app_default and cover_inverted false are stored as an absent column, which is what the settings window of the application writes for them, and a read publishes the same values back. colors does not change size.

if_version is not accepted, and the call is never confirmed: its inverse is /v1/brand-templates/delete.

Refusals: 400 for the framework and the body (VALIDATION_FAILED, one entry per field), 403 WRITE_ACCESS_DISABLED.

A brand template written through this API is not visible to an application window that is already open: the page has to be reloaded before the same template is edited there.

Every write endpoint reads dry_run and idempotency_key; see the Write guarantees section of the introduction.

## Request body

- BrandTemplatesCreateRequest: Every setting is optional: what a body does not carry takes the default of the product, and a new template is born with the logo of the platform.
  - `name` string, nullable, required
  - `internal_notes` string, nullable: A note for the account's own staff. Left out or null, it is left as it is.
  - `font_family` string, nullable: The font of the PDF. Left out or null, it is left as it is.
  - `margin_top` integer, nullable: The top margin of the page, in points. Left out or null, it is left as it is.
  - `margin_bottom` integer, nullable: The bottom margin of the page, in points. Left out or null, it is left as it is.
  - `margin_left` integer, nullable: The left margin of the page, in points. Left out or null, it is left as it is.
  - `margin_right` integer, nullable: The right margin of the page, in points. Left out or null, it is left as it is.
  - `report_title_text_color` string, nullable: The colour of the title of the cover, as #RRGGBB. Left out or null, it is left as it is.
  - `report_title_font_size` integer, nullable: The size of the title of the cover. Left out or null, it is left as it is.
  - `report_title_font_weight` 'normal' | 'bold', nullable: The weight of the title of the cover. Left out or null, it is left as it is.
  - `report_subtitle_text_color` string, nullable: The colour of the subtitle of the cover, as #RRGGBB. Left out or null, it is left as it is.
  - `report_subtitle_font_size` integer, nullable: The size of the subtitle of the cover. Left out or null, it is left as it is.
  - `report_subtitle_font_weight` 'normal' | 'bold', nullable: The weight of the subtitle of the cover. Left out or null, it is left as it is.
  - `section_name_text_color` string, nullable: The colour of the name of a section, as #RRGGBB. Left out or null, it is left as it is.
  - `section_name_font_size` integer, nullable: The size of the name of a section. Left out or null, it is left as it is.
  - `section_name_font_weight` 'normal' | 'bold', nullable: The weight of the name of a section. Left out or null, it is left as it is.
  - `date_range_text_color` string, nullable: The colour of the period line of a section, as #RRGGBB. Left out or null, it is left as it is.
  - `date_range_font_size` integer, nullable: The size of the period line of a section. Left out or null, it is left as it is.
  - `date_range_font_weight` 'normal' | 'bold', nullable: The weight of the period line of a section. Left out or null, it is left as it is.
  - `component_name_text_color` string, nullable: The colour of the name of a widget, as #RRGGBB. Left out or null, it is left as it is.
  - `component_name_font_size` integer, nullable: The size of the name of a widget. Left out or null, it is left as it is.
  - `component_name_font_weight` 'normal' | 'bold', nullable: The weight of the name of a widget. Left out or null, it is left as it is.
  - `footer_text_color` string, nullable: The colour of the footer, as #RRGGBB. Left out or null, it is left as it is.
  - `footer_font_size` integer, nullable: The size of the footer. Left out or null, it is left as it is.
  - `footer_font_weight` 'normal' | 'bold', nullable: The weight of the footer. Left out or null, it is left as it is.
  - `include_logo` boolean, nullable: Whether the report prints the logo. It cannot be true on a template that carries none, and setting it FALSE REMOVES the file, which is what the settings window of the application does when the checkbox is unticked: nothing keeps a copy, so switching it on again needs a new upload through /v1/brand-templates/set-logo. The answer says so with a LOGO_REMOVED warning. Left out or null, it is left as it is.
  - `include_section_line` boolean, nullable: Whether a line is drawn under the name of a section. Left out or null, it is left as it is.
  - `section_line_color` string, nullable: The colour of that line, as #RRGGBB. Left out or null, it is left as it is.
  - `component_name_background_color` string, nullable: The background behind the name of a widget, as #RRGGBB. Left out or null, it is left as it is.
  - `component_content_text_color` string, nullable: The colour of the content of a widget, as #RRGGBB. Left out or null, it is left as it is.
  - `component_content_font_size` integer, nullable: The size of the content of a widget. Left out or null, it is left as it is.
  - `colors` string[], nullable: The palette the charts are drawn with, in order, as #RRGGBB. Left out or null, it is left as it is.
  - `footer_content` string, nullable: The text of the footer. Left out or null, it is left as it is.
  - `include_footer_line` boolean, nullable: Whether a line is drawn above the footer. Left out or null, it is left as it is.
  - `footer_line_color` string, nullable: The colour of that line, as #RRGGBB. Left out or null, it is left as it is.
  - `primary_color` string, nullable: The main colour of the brand, as #RRGGBB. Left out or null, it is left as it is.
  - `cover_style` 'gradient' | 'plain' | 'aurora' | 'circles' | 'waves' | 'diagonal' | 'rings' | 'dots' | 'bubbles' | 'contours' | 'blocks' | 'image', nullable: The style the front cover is drawn with. A template that never picked one is read as plain, which is what it is drawn with. Left out or null, it is left as it is.
  - `cover_tone` 'auto' | 'light' | 'dark' | 'custom', nullable: The tone of the text of the cover. auto takes it from the primary colour and is what a template that never picked one is read as. Left out or null, it is left as it is.
  - `cover_text_color` string, nullable: The colour of the cover text when the tone is custom, as #RRGGBB. Left out or null, it is left as it is.
  - `cover_inverted` boolean, nullable: Whether the cover is drawn on a coloured surface instead of white paper. Left out or null, it is left as it is.
  - `prepared_by` string, nullable: The line naming who prepared the report. Left out or null, it is left as it is.
  - `prepared_for` string, nullable: The line naming who it was prepared for. Left out or null, it is left as it is.
  - `toc_style` 'cards' | 'rule' | 'band' | 'timeline', nullable: The style of the table of contents. A template that never picked one is read as timeline. Left out or null, it is left as it is.
  - `portal_header_style` 'none' | 'letterhead' | 'card' | 'band', nullable: The header the client portal wears. none draws no header and is what a template that never picked one is read as. Left out or null, it is left as it is.
  - `portal_header_logo` boolean, nullable: Whether that header carries the logo. Left out or null, it is left as it is.
  - `web_font_family` string, nullable: The font of the client portal and of a public link. app_default is the font of the application. Left out or null, it is left as it is.
  - `brand_template_key` string, nullable: Ignored on a write: part of what a read publishes and not part of what a write stores. It is accepted so the object /v1/brand-templates/get answers can be sent straight back, and it comes back as a READ_ONLY_FIELD_IGNORED warning.
  - `is_system` boolean, nullable: Ignored on a write: part of what a read publishes and not part of what a write stores. It is accepted so the object /v1/brand-templates/get answers can be sent straight back, and it comes back as a READ_ONLY_FIELD_IGNORED warning.
  - `logo` object, nullable: Ignored on a write: part of what a read publishes and not part of what a write stores. It is accepted so the object /v1/brand-templates/get answers can be sent straight back, and it comes back as a READ_ONLY_FIELD_IGNORED warning.
  - `cover_image` object, nullable: Ignored on a write: part of what a read publishes and not part of what a write stores. It is accepted so the object /v1/brand-templates/get answers can be sent straight back, and it comes back as a READ_ONLY_FIELD_IGNORED warning.
  - `dry_run` boolean: Validate everything and write nothing.
  - `idempotency_key` string: Replays the answer of an identical call for 24 hours.

## Response `200`

The call succeeded. This endpoint reports its errors with a real HTTP status, so a 200 always carries status=ok.

- BrandTemplatesCreateResponse: The 200 of this write, in whichever of its shapes the call asked for.
  - `status` 'ok', required
  - `data` union, required: The answer of the write. A call sent with dry_run: true answers WriteDryRunData instead, and the call that asks for a confirmation answers WriteConfirmationRequiredData. Those two carry the preview of this operation alongside their own fields, so an answer can match more than one of these shapes.
    - BrandTemplatesCreateData: The brand template that was written, read back in the shape /v1/brand-templates/get publishes.
      - `brand_template` BrandTemplate, required: Every setting of one brand template. The two images are reported as present or absent and never carried.
        - `brand_template_key` string, required: The brand template. The empty string is the brand of the platform.
        - `is_system` boolean, required: Whether this is the brand of the platform rather than one of the account.
        - `name` string, nullable, required
        - `internal_notes` string, nullable: A note for the account's own staff.
        - `font_family` string, nullable: The font of the PDF.
        - `margin_top` integer, nullable: The top margin of the page, in points.
        - `margin_bottom` integer, nullable: The bottom margin of the page, in points.
        - `margin_left` integer, nullable: The left margin of the page, in points.
        - `margin_right` integer, nullable: The right margin of the page, in points.
        - `report_title_text_color` string, nullable: The colour of the title of the cover, as #RRGGBB.
        - `report_title_font_size` integer, nullable: The size of the title of the cover.
        - `report_title_font_weight` 'normal' | 'bold', nullable: The weight of the title of the cover.
        - `report_subtitle_text_color` string, nullable: The colour of the subtitle of the cover, as #RRGGBB.
        - `report_subtitle_font_size` integer, nullable: The size of the subtitle of the cover.
        - `report_subtitle_font_weight` 'normal' | 'bold', nullable: The weight of the subtitle of the cover.
        - `section_name_text_color` string, nullable: The colour of the name of a section, as #RRGGBB.
        - `section_name_font_size` integer, nullable: The size of the name of a section.
        - `section_name_font_weight` 'normal' | 'bold', nullable: The weight of the name of a section.
        - `date_range_text_color` string, nullable: The colour of the period line of a section, as #RRGGBB.
        - `date_range_font_size` integer, nullable: The size of the period line of a section.
        - `date_range_font_weight` 'normal' | 'bold', nullable: The weight of the period line of a section.
        - `component_name_text_color` string, nullable: The colour of the name of a widget, as #RRGGBB.
        - `component_name_font_size` integer, nullable: The size of the name of a widget.
        - `component_name_font_weight` 'normal' | 'bold', nullable: The weight of the name of a widget.
        - `footer_text_color` string, nullable: The colour of the footer, as #RRGGBB.
        - `footer_font_size` integer, nullable: The size of the footer.
        - `footer_font_weight` 'normal' | 'bold', nullable: The weight of the footer.
        - `include_logo` boolean: Whether the report prints the logo.
        - `include_section_line` boolean: Whether a line is drawn under the name of a section.
        - `section_line_color` string, nullable: The colour of that line, as #RRGGBB.
        - `component_name_background_color` string, nullable: The background behind the name of a widget, as #RRGGBB.
        - `component_content_text_color` string, nullable: The colour of the content of a widget, as #RRGGBB.
        - `component_content_font_size` integer, nullable: The size of the content of a widget.
        - `colors` string[], required: The palette the charts are drawn with, in order, as #RRGGBB.
        - `footer_content` string, nullable: The text of the footer.
        - `include_footer_line` boolean: Whether a line is drawn above the footer.
        - `footer_line_color` string, nullable: The colour of that line, as #RRGGBB.
        - `primary_color` string, nullable: The main colour of the brand, as #RRGGBB.
        - `cover_style` 'gradient' | 'plain' | 'aurora' | 'circles' | 'waves' | 'diagonal' | 'rings' | 'dots' | 'bubbles' | 'contours' | 'blocks' | 'image': The style the front cover is drawn with. A template that never picked one is read as plain, which is what it is drawn with.
        - `cover_tone` 'auto' | 'light' | 'dark' | 'custom': The tone of the text of the cover. auto takes it from the primary colour and is what a template that never picked one is read as.
        - `cover_text_color` string, nullable: The colour of the cover text when the tone is custom, as #RRGGBB.
        - `cover_inverted` boolean: Whether the cover is drawn on a coloured surface instead of white paper.
        - `prepared_by` string, nullable: The line naming who prepared the report.
        - `prepared_for` string, nullable: The line naming who it was prepared for.
        - `toc_style` 'cards' | 'rule' | 'band' | 'timeline': The style of the table of contents. A template that never picked one is read as timeline.
        - `portal_header_style` 'none' | 'letterhead' | 'card' | 'band': The header the client portal wears. none draws no header and is what a template that never picked one is read as.
        - `portal_header_logo` boolean: Whether that header carries the logo.
        - `web_font_family` string: The font of the client portal and of a public link. app_default is the font of the application.
        - `logo` BrandTemplateImage, required: An image the template carries. The file itself never travels in JSON and it is not read to be measured, so no size is published.
          - `present` boolean, required: Always true. The whole object is null when there is no image.
        - `cover_image` BrandTemplateImage, required: An image the template carries. The file itself never travels in JSON and it is not read to be measured, so no size is published.
          - `present` boolean, required: Always true. The whole object is null when there is no image.
      - `created_keys` string[]: The key of the brand template, as every creation of this API publishes it.
      - `unfulfilled` Problem[], required
        - `path` string, required: Dotted path of the offending field inside the request body, empty for a problem about the body as a whole.
        - `code` 'MISSING_REQUIRED_FIELD' | 'INVALID_VALUE' | 'UNKNOWN_PARAM' | 'INVALID_DATE_RANGE' | 'INTEGRATION_NOT_SUPPORTED' | 'CONNECTION_NOT_FOUND' | 'ACCOUNT_NOT_FOUND' | 'ONE_CONNECTION_PER_INTEGRATION' | 'DATE_PRESET_NOT_IN_REPORTS' | 'TEMPLATE_NOT_FOUND' | 'TEMPLATE_PARAMETERIZED' | 'ROW_RULE' | 'POSITION_CHANGED' | 'SECTION_CHANGED' | 'TOO_MANY_PROBLEMS' | 'DATA_SOURCES_PARTIALLY_BOUND' | 'WELCOME_EMAIL_FAILED' | 'REPORT_WILL_BE_SHARED_PUBLICLY' | 'MAY_BE_DELIVERED_AGAIN_TODAY' | 'SUMMARY_SECTION_SKIPPED' | 'COVER_FILE_REMOVED' | 'COVER_TYPE_SET' | 'LOGO_REMOVED' | 'READ_ONLY_FIELD_IGNORED' | 'AI_BRIEF_NOT_SAVED' | 'SPENDS_AI_CREDITS' | 'DROPS_ACCOUNT_CACHE' | 'CHART_TYPE_NOT_FOR_THIS_SHAPE' | 'CHART_TYPE_NOT_FOR_TREND' | 'CONTENT_NOT_FOR_THIS_SHAPE' | 'FULL_WIDTH_NOT_FOR_KPI' | 'DATA_VIEW_FIELDS_PER_ACCOUNT' | 'DATA_VIEW_NOT_AVAILABLE' | 'DATA_VIEW_NOT_EMPTY' | 'DATA_VIEW_NOT_OF_THE_SECTION' | 'DATA_VIEW_REQUIRED' | 'DATA_VIEW_UNKNOWN' | 'DATE_RANGE_CUSTOM_INCOMPLETE' | 'DATE_RANGE_CUSTOM_INVALID' | 'DATE_RANGE_CUSTOM_INVERTED' | 'DATE_RANGE_PRESET_UNKNOWN' | 'DATE_RANGE_X_REQUIRED' | 'DIMENSION_DUPLICATED' | 'DIMENSION_IS_A_METRIC' | 'DIMENSION_REQUIRED' | 'DIMENSION_UNKNOWN' | 'DSL_INVALID' | 'FIELD_NOT_IN_AD_TYPES' | 'FILTERS_NOT_SUPPORTED' | 'FILTER_COMBINATION_NOT_SUPPORTED' | 'FILTER_CONDITIONS_MIXED_KINDS' | 'FILTER_FIELD_NOT_A_COLUMN' | 'FILTER_FIELD_NOT_IN_DATA_VIEW' | 'FILTER_FIELD_UNKNOWN' | 'FILTER_LIKE_NEEDS_WILDCARD' | 'FILTER_OPERATOR_INVALID' | 'FILTER_TYPE_OF_FIELD_MISMATCH' | 'FILTER_TYPE_OF_FIELD_NOT_USED' | 'FILTER_TYPE_OF_FIELD_REQUIRED' | 'FILTER_VALUE_EMPTY' | 'FILTER_VALUE_NOT_ALLOWED' | 'FILTER_VALUE_NOT_NUMERIC' | 'GRAPH_EXTRA_DIMENSIONS' | 'GRAPH_TIME_DIMENSION_MIXED' | 'INTEGRATION_UNKNOWN' | 'INTENT_MISSING' | 'KPI_NEEDS_ONE_METRIC' | 'KPI_TAKES_NO_DIMENSIONS' | 'LIMIT_IGNORED_IN_TREND' | 'LIMIT_OUT_OF_RANGE' | 'METRICS_SUMMARY_TAKES_NO_DIMENSIONS' | 'METRIC_DUPLICATED' | 'METRIC_IS_A_DIMENSION' | 'METRIC_UNKNOWN' | 'METRICS_REQUIRED' | 'NAME_EMPTY' | 'NAME_NOT_PLAIN_TEXT' | 'NAME_TOO_LONG' | 'PIE_TAKES_ONE_DIMENSION' | 'PREVIOUS_PERIOD_NOT_AVAILABLE_FOR_THIS_TABLE' | 'COMPARISON_NOT_AVAILABLE_FOR_THIS_WIDGET' | 'PREVIOUS_PERIOD_NOT_FOR_THIS_SHAPE' | 'SHOW_CHANGE_NOT_FOR_THIS_SHAPE' | 'SHOW_CHANGE_NEEDS_PREVIOUS_PERIOD' | 'SETTING_DUPLICATED' | 'SETTING_NOT_FOR_DATA_VIEW' | 'SETTING_NOT_IN_WIDGET' | 'SETTING_UNKNOWN' | 'SETTING_VALUE_INVALID' | 'SORT_FIELD_NOT_SELECTED' | 'TABLE_TIME_DIMENSION_NOT_DRAWABLE' | 'TEXT_CONTENT_REQUIRED' | 'TEXT_CONTENT_TOO_LONG' | 'TEXT_NOT_A_WIDGET_TYPE' | 'TEXT_TAKES_NO_DATA' | 'TOO_MANY_DIMENSIONS' | 'TOO_MANY_METRICS' | 'TRANSLATION_FAILED' | 'TREND_ORDER_IS_THE_TIME_AXIS' | 'TREND_TAKES_NO_DIMENSIONS' | 'VISUALIZATION_NOT_AVAILABLE' | 'VISUALIZATION_NOT_OF_REQUESTED_TYPE' | 'FIELD_NOT_AVAILABLE' | 'FILTER_CONDITIONS_MIXED_LEVELS' | 'FILTER_LEVEL_MISMATCH' | 'FORMULA_TOKEN_UNKNOWN' | 'FORMULA_INVALID' | 'LANGUAGE_FILE_KEYS_IGNORED' | 'SELECTION_EMPTY' | 'FILTER_KEY_AND_FILTERS' | 'FILTER_KEY_NOT_FOR_THIS_SHAPE' | 'KEY_NOT_FOR_GENERATION' | 'COMPARE_NOT_FOR_THIS_SHAPE' | 'COMPARE_NOT_FOR_INTEGRATION' | 'COMPARE_ITEMS_CARDINALITY' | 'COMPARE_METRICS_CARDINALITY' | 'COMPARE_OVER_TIME_NOT_AVAILABLE' | 'COMPARE_METRIC_HAS_NO_BREAKDOWN' | 'COMPARE_KEY_NOT_FOR_THIS_COMPARATOR' | 'COMPARATOR_CLASS_FIXED', required: What is wrong with that field, or what is worth knowing about it. The first group is the vocabulary of the body itself; the eleven that follow it appear only in `warnings[]`, bar TOO_MANY_PROBLEMS, which closes a `problems` list that was truncated; and the rest are the codes of the widget DSL, which travel here when a composition or a widget carries a dsl that does not validate.
        - `message` string, required: Human-readable explanation. Not stable across versions.
        - `suggestions` string[]: The accepted values, when the field takes a closed list.
      - `warnings` Problem[], required
        - `path` string, required: Dotted path of the offending field inside the request body, empty for a problem about the body as a whole.
        - `code` 'MISSING_REQUIRED_FIELD' | 'INVALID_VALUE' | 'UNKNOWN_PARAM' | 'INVALID_DATE_RANGE' | 'INTEGRATION_NOT_SUPPORTED' | 'CONNECTION_NOT_FOUND' | 'ACCOUNT_NOT_FOUND' | 'ONE_CONNECTION_PER_INTEGRATION' | 'DATE_PRESET_NOT_IN_REPORTS' | 'TEMPLATE_NOT_FOUND' | 'TEMPLATE_PARAMETERIZED' | 'ROW_RULE' | 'POSITION_CHANGED' | 'SECTION_CHANGED' | 'TOO_MANY_PROBLEMS' | 'DATA_SOURCES_PARTIALLY_BOUND' | 'WELCOME_EMAIL_FAILED' | 'REPORT_WILL_BE_SHARED_PUBLICLY' | 'MAY_BE_DELIVERED_AGAIN_TODAY' | 'SUMMARY_SECTION_SKIPPED' | 'COVER_FILE_REMOVED' | 'COVER_TYPE_SET' | 'LOGO_REMOVED' | 'READ_ONLY_FIELD_IGNORED' | 'AI_BRIEF_NOT_SAVED' | 'SPENDS_AI_CREDITS' | 'DROPS_ACCOUNT_CACHE' | 'CHART_TYPE_NOT_FOR_THIS_SHAPE' | 'CHART_TYPE_NOT_FOR_TREND' | 'CONTENT_NOT_FOR_THIS_SHAPE' | 'FULL_WIDTH_NOT_FOR_KPI' | 'DATA_VIEW_FIELDS_PER_ACCOUNT' | 'DATA_VIEW_NOT_AVAILABLE' | 'DATA_VIEW_NOT_EMPTY' | 'DATA_VIEW_NOT_OF_THE_SECTION' | 'DATA_VIEW_REQUIRED' | 'DATA_VIEW_UNKNOWN' | 'DATE_RANGE_CUSTOM_INCOMPLETE' | 'DATE_RANGE_CUSTOM_INVALID' | 'DATE_RANGE_CUSTOM_INVERTED' | 'DATE_RANGE_PRESET_UNKNOWN' | 'DATE_RANGE_X_REQUIRED' | 'DIMENSION_DUPLICATED' | 'DIMENSION_IS_A_METRIC' | 'DIMENSION_REQUIRED' | 'DIMENSION_UNKNOWN' | 'DSL_INVALID' | 'FIELD_NOT_IN_AD_TYPES' | 'FILTERS_NOT_SUPPORTED' | 'FILTER_COMBINATION_NOT_SUPPORTED' | 'FILTER_CONDITIONS_MIXED_KINDS' | 'FILTER_FIELD_NOT_A_COLUMN' | 'FILTER_FIELD_NOT_IN_DATA_VIEW' | 'FILTER_FIELD_UNKNOWN' | 'FILTER_LIKE_NEEDS_WILDCARD' | 'FILTER_OPERATOR_INVALID' | 'FILTER_TYPE_OF_FIELD_MISMATCH' | 'FILTER_TYPE_OF_FIELD_NOT_USED' | 'FILTER_TYPE_OF_FIELD_REQUIRED' | 'FILTER_VALUE_EMPTY' | 'FILTER_VALUE_NOT_ALLOWED' | 'FILTER_VALUE_NOT_NUMERIC' | 'GRAPH_EXTRA_DIMENSIONS' | 'GRAPH_TIME_DIMENSION_MIXED' | 'INTEGRATION_UNKNOWN' | 'INTENT_MISSING' | 'KPI_NEEDS_ONE_METRIC' | 'KPI_TAKES_NO_DIMENSIONS' | 'LIMIT_IGNORED_IN_TREND' | 'LIMIT_OUT_OF_RANGE' | 'METRICS_SUMMARY_TAKES_NO_DIMENSIONS' | 'METRIC_DUPLICATED' | 'METRIC_IS_A_DIMENSION' | 'METRIC_UNKNOWN' | 'METRICS_REQUIRED' | 'NAME_EMPTY' | 'NAME_NOT_PLAIN_TEXT' | 'NAME_TOO_LONG' | 'PIE_TAKES_ONE_DIMENSION' | 'PREVIOUS_PERIOD_NOT_AVAILABLE_FOR_THIS_TABLE' | 'COMPARISON_NOT_AVAILABLE_FOR_THIS_WIDGET' | 'PREVIOUS_PERIOD_NOT_FOR_THIS_SHAPE' | 'SHOW_CHANGE_NOT_FOR_THIS_SHAPE' | 'SHOW_CHANGE_NEEDS_PREVIOUS_PERIOD' | 'SETTING_DUPLICATED' | 'SETTING_NOT_FOR_DATA_VIEW' | 'SETTING_NOT_IN_WIDGET' | 'SETTING_UNKNOWN' | 'SETTING_VALUE_INVALID' | 'SORT_FIELD_NOT_SELECTED' | 'TABLE_TIME_DIMENSION_NOT_DRAWABLE' | 'TEXT_CONTENT_REQUIRED' | 'TEXT_CONTENT_TOO_LONG' | 'TEXT_NOT_A_WIDGET_TYPE' | 'TEXT_TAKES_NO_DATA' | 'TOO_MANY_DIMENSIONS' | 'TOO_MANY_METRICS' | 'TRANSLATION_FAILED' | 'TREND_ORDER_IS_THE_TIME_AXIS' | 'TREND_TAKES_NO_DIMENSIONS' | 'VISUALIZATION_NOT_AVAILABLE' | 'VISUALIZATION_NOT_OF_REQUESTED_TYPE' | 'FIELD_NOT_AVAILABLE' | 'FILTER_CONDITIONS_MIXED_LEVELS' | 'FILTER_LEVEL_MISMATCH' | 'FORMULA_TOKEN_UNKNOWN' | 'FORMULA_INVALID' | 'LANGUAGE_FILE_KEYS_IGNORED' | 'SELECTION_EMPTY' | 'FILTER_KEY_AND_FILTERS' | 'FILTER_KEY_NOT_FOR_THIS_SHAPE' | 'KEY_NOT_FOR_GENERATION' | 'COMPARE_NOT_FOR_THIS_SHAPE' | 'COMPARE_NOT_FOR_INTEGRATION' | 'COMPARE_ITEMS_CARDINALITY' | 'COMPARE_METRICS_CARDINALITY' | 'COMPARE_OVER_TIME_NOT_AVAILABLE' | 'COMPARE_METRIC_HAS_NO_BREAKDOWN' | 'COMPARE_KEY_NOT_FOR_THIS_COMPARATOR' | 'COMPARATOR_CLASS_FIXED', required: What is wrong with that field, or what is worth knowing about it. The first group is the vocabulary of the body itself; the eleven that follow it appear only in `warnings[]`, bar TOO_MANY_PROBLEMS, which closes a `problems` list that was truncated; and the rest are the codes of the widget DSL, which travel here when a composition or a widget carries a dsl that does not validate.
        - `message` string, required: Human-readable explanation. Not stable across versions.
        - `suggestions` string[]: The accepted values, when the field takes a closed list.
    - WriteDryRunData: The answer of a call sent with dry_run: true. Nothing was written, no idempotency key was consumed and no confirmation token was burnt. When valid is true, the fields of the endpoint's own preview travel alongside these two; when it is false, `message` and `problems` do instead. The HTTP status is 200 in both cases: a rehearsal that says the body is wrong is a successful rehearsal. Two refusals are NOT translated and arrive with their real status: a 429 (the budgets are spent either way) and any 5xx.
      - `dry_run` true, required: Always true in this shape.
      - `valid` boolean, required: Whether the body would be accepted.
      - `message` string: Why the body would be refused. Present only when valid is false.
      - `problems` Problem[]: Everything that is wrong with the body. Present only when valid is false.
        - `path` string, required: Dotted path of the offending field inside the request body, empty for a problem about the body as a whole.
        - `code` 'MISSING_REQUIRED_FIELD' | 'INVALID_VALUE' | 'UNKNOWN_PARAM' | 'INVALID_DATE_RANGE' | 'INTEGRATION_NOT_SUPPORTED' | 'CONNECTION_NOT_FOUND' | 'ACCOUNT_NOT_FOUND' | 'ONE_CONNECTION_PER_INTEGRATION' | 'DATE_PRESET_NOT_IN_REPORTS' | 'TEMPLATE_NOT_FOUND' | 'TEMPLATE_PARAMETERIZED' | 'ROW_RULE' | 'POSITION_CHANGED' | 'SECTION_CHANGED' | 'TOO_MANY_PROBLEMS' | 'DATA_SOURCES_PARTIALLY_BOUND' | 'WELCOME_EMAIL_FAILED' | 'REPORT_WILL_BE_SHARED_PUBLICLY' | 'MAY_BE_DELIVERED_AGAIN_TODAY' | 'SUMMARY_SECTION_SKIPPED' | 'COVER_FILE_REMOVED' | 'COVER_TYPE_SET' | 'LOGO_REMOVED' | 'READ_ONLY_FIELD_IGNORED' | 'AI_BRIEF_NOT_SAVED' | 'SPENDS_AI_CREDITS' | 'DROPS_ACCOUNT_CACHE' | 'CHART_TYPE_NOT_FOR_THIS_SHAPE' | 'CHART_TYPE_NOT_FOR_TREND' | 'CONTENT_NOT_FOR_THIS_SHAPE' | 'FULL_WIDTH_NOT_FOR_KPI' | 'DATA_VIEW_FIELDS_PER_ACCOUNT' | 'DATA_VIEW_NOT_AVAILABLE' | 'DATA_VIEW_NOT_EMPTY' | 'DATA_VIEW_NOT_OF_THE_SECTION' | 'DATA_VIEW_REQUIRED' | 'DATA_VIEW_UNKNOWN' | 'DATE_RANGE_CUSTOM_INCOMPLETE' | 'DATE_RANGE_CUSTOM_INVALID' | 'DATE_RANGE_CUSTOM_INVERTED' | 'DATE_RANGE_PRESET_UNKNOWN' | 'DATE_RANGE_X_REQUIRED' | 'DIMENSION_DUPLICATED' | 'DIMENSION_IS_A_METRIC' | 'DIMENSION_REQUIRED' | 'DIMENSION_UNKNOWN' | 'DSL_INVALID' | 'FIELD_NOT_IN_AD_TYPES' | 'FILTERS_NOT_SUPPORTED' | 'FILTER_COMBINATION_NOT_SUPPORTED' | 'FILTER_CONDITIONS_MIXED_KINDS' | 'FILTER_FIELD_NOT_A_COLUMN' | 'FILTER_FIELD_NOT_IN_DATA_VIEW' | 'FILTER_FIELD_UNKNOWN' | 'FILTER_LIKE_NEEDS_WILDCARD' | 'FILTER_OPERATOR_INVALID' | 'FILTER_TYPE_OF_FIELD_MISMATCH' | 'FILTER_TYPE_OF_FIELD_NOT_USED' | 'FILTER_TYPE_OF_FIELD_REQUIRED' | 'FILTER_VALUE_EMPTY' | 'FILTER_VALUE_NOT_ALLOWED' | 'FILTER_VALUE_NOT_NUMERIC' | 'GRAPH_EXTRA_DIMENSIONS' | 'GRAPH_TIME_DIMENSION_MIXED' | 'INTEGRATION_UNKNOWN' | 'INTENT_MISSING' | 'KPI_NEEDS_ONE_METRIC' | 'KPI_TAKES_NO_DIMENSIONS' | 'LIMIT_IGNORED_IN_TREND' | 'LIMIT_OUT_OF_RANGE' | 'METRICS_SUMMARY_TAKES_NO_DIMENSIONS' | 'METRIC_DUPLICATED' | 'METRIC_IS_A_DIMENSION' | 'METRIC_UNKNOWN' | 'METRICS_REQUIRED' | 'NAME_EMPTY' | 'NAME_NOT_PLAIN_TEXT' | 'NAME_TOO_LONG' | 'PIE_TAKES_ONE_DIMENSION' | 'PREVIOUS_PERIOD_NOT_AVAILABLE_FOR_THIS_TABLE' | 'COMPARISON_NOT_AVAILABLE_FOR_THIS_WIDGET' | 'PREVIOUS_PERIOD_NOT_FOR_THIS_SHAPE' | 'SHOW_CHANGE_NOT_FOR_THIS_SHAPE' | 'SHOW_CHANGE_NEEDS_PREVIOUS_PERIOD' | 'SETTING_DUPLICATED' | 'SETTING_NOT_FOR_DATA_VIEW' | 'SETTING_NOT_IN_WIDGET' | 'SETTING_UNKNOWN' | 'SETTING_VALUE_INVALID' | 'SORT_FIELD_NOT_SELECTED' | 'TABLE_TIME_DIMENSION_NOT_DRAWABLE' | 'TEXT_CONTENT_REQUIRED' | 'TEXT_CONTENT_TOO_LONG' | 'TEXT_NOT_A_WIDGET_TYPE' | 'TEXT_TAKES_NO_DATA' | 'TOO_MANY_DIMENSIONS' | 'TOO_MANY_METRICS' | 'TRANSLATION_FAILED' | 'TREND_ORDER_IS_THE_TIME_AXIS' | 'TREND_TAKES_NO_DIMENSIONS' | 'VISUALIZATION_NOT_AVAILABLE' | 'VISUALIZATION_NOT_OF_REQUESTED_TYPE' | 'FIELD_NOT_AVAILABLE' | 'FILTER_CONDITIONS_MIXED_LEVELS' | 'FILTER_LEVEL_MISMATCH' | 'FORMULA_TOKEN_UNKNOWN' | 'FORMULA_INVALID' | 'LANGUAGE_FILE_KEYS_IGNORED' | 'SELECTION_EMPTY' | 'FILTER_KEY_AND_FILTERS' | 'FILTER_KEY_NOT_FOR_THIS_SHAPE' | 'KEY_NOT_FOR_GENERATION' | 'COMPARE_NOT_FOR_THIS_SHAPE' | 'COMPARE_NOT_FOR_INTEGRATION' | 'COMPARE_ITEMS_CARDINALITY' | 'COMPARE_METRICS_CARDINALITY' | 'COMPARE_OVER_TIME_NOT_AVAILABLE' | 'COMPARE_METRIC_HAS_NO_BREAKDOWN' | 'COMPARE_KEY_NOT_FOR_THIS_COMPARATOR' | 'COMPARATOR_CLASS_FIXED', required: What is wrong with that field, or what is worth knowing about it. The first group is the vocabulary of the body itself; the eleven that follow it appear only in `warnings[]`, bar TOO_MANY_PROBLEMS, which closes a `problems` list that was truncated; and the rest are the codes of the widget DSL, which travel here when a composition or a widget carries a dsl that does not validate.
        - `message` string, required: Human-readable explanation. Not stable across versions.
        - `suggestions` string[]: The accepted values, when the field takes a closed list.
    - WriteConfirmationRequiredData: The answer of the first call to an endpoint that asks for a confirmation. Nothing was written. The fields of the endpoint's own preview travel alongside these three, so the caller can show what is about to happen. The token is good for one use, for this account, this endpoint and this exact body.
      - `confirmation_required` true, required: Always true in this shape.
      - `confirmation_token` string, required: Send the same body again with this token to perform the write.
      - `expires_at` string, date-time, required: When the token stops being accepted, 10 minutes after it was issued.
  - `meta` WriteMeta, required: The meta of a write answer. A replay carries two extra fields so it can be told apart from a first call and still traced back to it.
    - `request_id` string, required: Id of THIS request, including on a replay.
    - `timestamp` string, date-time, required
    - `replayed` boolean: True when this answer is the stored answer of an earlier call with the same idempotency_key and the same body. Absent on a first call.
    - `replayed_from_request_id` string, nullable: The request_id of the call that actually did the work. Present only on a replay, and null when the stored answer carried no id.

## Other responses

- `400`: The request was refused before anything was written. INVALID_REQUEST covers the framework refusals of every endpoint (the Content-Type is not application/json, the body could not be read, the body is not valid JSON). The report endpoints add their own: VALIDATION_FAILED with details.problems, UNKNOWN_PARAM with the path of the field the endpoint does not read, and the single-field refusals MISSING_REQUIRED_FIELD, INVALID_VALUE and APPEND_NOT_ALLOWED. The description of each operation lists the ones it emits. Everything else a body can get wrong arrives as an entry of details.problems rather than as the code of the envelope.
- `401`: Invalid or missing Bearer token.
- `403`: The account may not perform this write. WRITE_ACCESS_DISABLED means write access has not been enabled for the API key in the REST API tab of the app. PLAN_LIMIT_REACHED means the plan has no room left for another report or template, and is emitted only by the endpoints that create one.
- `404`: A key of the request does not name anything this account owns. NOT_FOUND covers reports, sections, widgets, schedules, portal views, brand templates, email templates and language files; TEMPLATE_NOT_FOUND covers a catalog slug, version or section position that stopped being published between the validation and the write. An entity of another account is reported the same way as one that does not exist.
- `409`: The request is well formed and the current state does not allow it. Every write can answer IDEMPOTENCY_CONFLICT (the same idempotency_key with a different body) and IDEMPOTENCY_IN_PROGRESS (an identical call is running, with a Retry-After). The endpoints that ask for a confirmation add CONFIRMATION_EXPIRED and CONFIRMATION_MISMATCH; the ones that never confirm cannot answer either. Each operation lists the domain conflicts it adds on top of those.
- `413`: The request body is over 1 MB. The cap is applied before authentication, so this answer is refused without the key being read, and it carries the usual error envelope.
- `429`: A budget was spent. RATE_LIMITED is the per-key read budget (300/minute, 5000/day) or the per-account write budget (60/minute, 1000/day). EMAIL_QUOTA_REACHED is the separate budget of 50 calls per account per day that send email, and its Retry-After points at the next UTC midnight. The Retry-After header gives the seconds until the more restrictive window resets. The X-RateLimit-* headers report the read budget, except on the 429 of a write, where they report the write budget.
- `500`: Internal server error.
- `503`: Writes are paused, or the store the write guarantees rely on is not answering. Nothing was written and the call is worth retrying later. Reads are unaffected.

---

[API](https://skmtc.dev/reportingninja/apis/reporting-ninja-api.md) · [All operations](https://skmtc.dev/reportingninja/apis/reporting-ninja-api/llms.txt) · [OpenAPI document](https://skmtc.dev/reportingninja/apis/reporting-ninja-api/revisions/13876c71be34?raw)
