---
title: "Update a calculated metric"
method: POST
path: "/calculated-metrics/update"
tags: ["Calculated Metrics"]
---

# Update a calculated metric

`POST /calculated-metrics/update`

## Request

- Base URL: `https://api.reportingninja.com/v1`
- URL: `https://api.reportingninja.com/v1/calculated-metrics/update`
- Auth: HTTP bearer

Changes a calculated metric of the account.

IT EMPTIES EVERY CACHED RESULT OF THE ACCOUNT, like /v1/filters/update and for the same reason: every widget that draws the metric was computed with the definition that has just changed. It is expensive on a large account, so do not chain calls of this endpoint. Creating and deleting a metric empty nothing.

It is a PARTIAL write, inside the block of its kind too: what the body does not name is left as it is. type, integration_id and level cannot change -- each kind is stored in columns of its own and the definition names fields of that data view -- and a body that names what the metric already is is accepted, so a read can be replayed.

ONLY is_system_defined STOPS AN UPDATE, which is what the editor of the application looks at and at nothing else: a row that carries the stable signature of a catalogue definition and is not marked as the platform's is edited on both surfaces. A metric the platform ships answers 409 NOT_ALLOWED with details.reason system_defined, decided before anything is written.

The two legacy formats a read may publish, time_millis and text, are kept by an update that does not name format and cannot be written: only the four of the editor are accepted.

The answer carries `before` with the whole previous definition, which is the body that undoes the call.

if_version is not accepted.

Refusals: 400 for the body and the formula, 403 WRITE_ACCESS_DISABLED, 404 NOT_FOUND, and 409 NOT_ALLOWED for a system one. Nothing is emptied in any of those cases: the cached results go only once the new definition has been stored, and a 500 that could not store it says so with details.dropped_account_cache false.

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

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

## Request body

- CalculatedMetricsUpdateRequest: Which calculated metric to change and what of it. Everything the body does not carry is left as it is, inside the block of its kind too. integration_id, type and level are accepted only when they name what the metric already is. The keys a read publishes and a write has no column for -- integration, is_system_defined, not_expressible_reason and not_expressible_message -- are declared here as readOnly: they are accepted, ignored and named one by one in a READ_ONLY_FIELD_IGNORED warning, so what a read answered can be sent straight back with nothing taken out of it. A null means "leave it as it is".
  - `calculated_metric_key` string, required: The metric to change. From /v1/calculated-metrics/list.
  - `name` string, nullable: How the definition is listed in the account. 2 to 50 characters, with no character above the Basic Multilingual Plane.
  - `display_name` string, nullable: What is drawn on a widget that uses it. Null or left out leaves it as it is.
  - `internal_notes` string, nullable: A note for the account's own staff, at most 250 characters. An empty string takes it away.
  - `integration_id` string, nullable: Only the integration the metric already has.
  - `level` string, nullable: Only the data view the metric already has.
  - `type` 'expression' | 'function' | 'filtered': Only the kind the metric already is.
  - `expression` object, nullable: Kind expression: the formula and how its result is printed.
    - `format` 'none' | 'time' | 'currency' | 'percentage' | 'null', nullable: Required on a creation. The two older values a read may publish, time_millis and text, are kept by an update that does not name format and cannot be written.
    - `formula` string[]: A flat list of infix tokens: metric ids of /v1/fields for this integration, the operators + - * /, '(' and ')', and literal numbers. At least one metric, the parentheses balance, and an id the field catalogue does not publish is refused -- which is stricter than the application, where a token is a metric as soon as it starts with the prefix of an integration.
    - `green_when_increases` boolean: Whether a rise of the metric is drawn as a good thing. True on a creation that does not name it.
  - `function` object, nullable: Kind function: one metric aggregated over a period.
    - `metric` string, nullable: A metric id of /v1/fields for this integration.
    - `function` 'average' | 'min' | 'max' | 'null', nullable
    - `time_period` 'day' | 'week' | 'month' | 'null', nullable
  - `filtered` object, nullable: Kind filtered: one metric read through a saved filter of the same integration.
    - `metric` string, nullable: A metric id of /v1/fields for this integration.
    - `filter_key` string, nullable: A saved filter of the account and of this integration. From /v1/filters/list.
  - `integration` string: The name of a data source this API publishes no integration_id for, which a read answers instead of integration_id. Published by /v1/calculated-metrics/get and written by nothing. It is accepted here so the object a read answers can be sent straight back as a body; the key is ignored and the answer carries a READ_ONLY_FIELD_IGNORED warning naming it.
  - `is_system_defined` boolean: Published by /v1/calculated-metrics/get and written by nothing. It is accepted here so the object a read answers can be sent straight back as a body; the key is ignored and the answer carries a READ_ONLY_FIELD_IGNORED warning naming it.
  - `not_expressible_reason` string, nullable: Published by /v1/calculated-metrics/get and written by nothing. It is accepted here so the object a read answers can be sent straight back as a body; the key is ignored and the answer carries a READ_ONLY_FIELD_IGNORED warning naming it.
  - `not_expressible_message` string, nullable: Published by /v1/calculated-metrics/get and written by nothing. It is accepted here so the object a read answers can be sent straight back as a body; the key is ignored and the answer carries a READ_ONLY_FIELD_IGNORED warning naming it.
  - `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.

- CalculatedMetricsUpdateResponse: 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.
    - CalculatedMetricsUpdateData: The metric as it is now, and what it was. Sending `before` back through this endpoint puts the definition as it was, and empties the cached results of the account again.
      - `calculated_metric` CalculatedMetric, required: One calculated metric of the account. Only the block of its own kind is filled in, and all three are null when the definition cannot be named in public.
        - `calculated_metric_key` string, nullable, required: The metric. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is. It is null in ONE place: the preview of a dry run of a creation, where the row does not exist yet and so has no key.
        - `name` string, nullable, required
        - `display_name` string, nullable: The name shown on a widget that uses it, when it differs from the name.
        - `internal_notes` string, nullable: A note for the account's own staff.
        - `integration_id` string, nullable, required: The integration the metric belongs to, or null when the data source has no public id.
        - `integration` string: The name the product gives a data source with no public integration_id. Present only in that case. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
        - `level` string, nullable, required: The data view the metric belongs to. Most metrics belong to none and are read as null; when the one they do belong to has no public name, level is null and not_expressible_reason says level_not_available.
        - `type` 'expression' | 'function' | 'filtered', nullable, required: Which of the three kinds it is.
        - `is_system_defined` boolean, required: Whether the metric is one the platform ships. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
        - `expression` CalculatedMetricExpression, required: A metric written as a formula.
          - `format` 'none' | 'time' | 'currency' | 'percentage' | 'time_millis' | 'text', nullable, required: How the result is formatted. time_millis and text are two older values outside the four the editor offers, published as they are stored.
          - `formula` string[], required: The formula as a flat list of infix tokens: the metric ids as the definition stores them, normally the ones /v1/fields publishes, plus the operators + - * /, parentheses and literal numbers. A metric the product retired can still appear in an older formula.
          - `green_when_increases` boolean, required: Whether a rise of the metric is shown as a good thing.
        - `function` CalculatedMetricFunction, required: A metric aggregated over a period.
          - `metric` string, nullable, required: The metric being aggregated, named as /v1/fields names it.
          - `function` 'average' | 'min' | 'max', nullable, required
          - `time_period` 'day' | 'week' | 'month', nullable, required
        - `filtered` CalculatedMetricFiltered, required: A metric read through a saved filter.
          - `metric` string, nullable, required: The metric being read, named as /v1/fields names it.
          - `filter_key` string, nullable, required: The saved filter it is read through. From /v1/filters/list. It is published as it is stored and may name a filter that was deleted, which /v1/filters/get answers with 404.
        - `not_expressible_reason` 'source_not_available' | 'level_not_available' | 'unreadable', nullable, required: Why the definition is not published as it stands. With source_not_available and with unreadable the three blocks are null; with level_not_available only level is, because the id of a metric does not depend on the data view it is read in. Null when there is nothing to report. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
        - `not_expressible_message` string, nullable: The same thing in a sentence. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
      - `before` CalculatedMetric, required: One calculated metric of the account. Only the block of its own kind is filled in, and all three are null when the definition cannot be named in public.
        - `calculated_metric_key` string, nullable, required: The metric. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is. It is null in ONE place: the preview of a dry run of a creation, where the row does not exist yet and so has no key.
        - `name` string, nullable, required
        - `display_name` string, nullable: The name shown on a widget that uses it, when it differs from the name.
        - `internal_notes` string, nullable: A note for the account's own staff.
        - `integration_id` string, nullable, required: The integration the metric belongs to, or null when the data source has no public id.
        - `integration` string: The name the product gives a data source with no public integration_id. Present only in that case. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
        - `level` string, nullable, required: The data view the metric belongs to. Most metrics belong to none and are read as null; when the one they do belong to has no public name, level is null and not_expressible_reason says level_not_available.
        - `type` 'expression' | 'function' | 'filtered', nullable, required: Which of the three kinds it is.
        - `is_system_defined` boolean, required: Whether the metric is one the platform ships. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
        - `expression` CalculatedMetricExpression, required: A metric written as a formula.
          - `format` 'none' | 'time' | 'currency' | 'percentage' | 'time_millis' | 'text', nullable, required: How the result is formatted. time_millis and text are two older values outside the four the editor offers, published as they are stored.
          - `formula` string[], required: The formula as a flat list of infix tokens: the metric ids as the definition stores them, normally the ones /v1/fields publishes, plus the operators + - * /, parentheses and literal numbers. A metric the product retired can still appear in an older formula.
          - `green_when_increases` boolean, required: Whether a rise of the metric is shown as a good thing.
        - `function` CalculatedMetricFunction, required: A metric aggregated over a period.
          - `metric` string, nullable, required: The metric being aggregated, named as /v1/fields names it.
          - `function` 'average' | 'min' | 'max', nullable, required
          - `time_period` 'day' | 'week' | 'month', nullable, required
        - `filtered` CalculatedMetricFiltered, required: A metric read through a saved filter.
          - `metric` string, nullable, required: The metric being read, named as /v1/fields names it.
          - `filter_key` string, nullable, required: The saved filter it is read through. From /v1/filters/list. It is published as it is stored and may name a filter that was deleted, which /v1/filters/get answers with 404.
        - `not_expressible_reason` 'source_not_available' | 'level_not_available' | 'unreadable', nullable, required: Why the definition is not published as it stands. With source_not_available and with unreadable the three blocks are null; with level_not_available only level is, because the id of a metric does not depend on the data view it is read in. Null when there is nothing to report. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
        - `not_expressible_message` string, nullable: The same thing in a sentence. Published by a read and never written: a body that carries it is accepted, ignored and named in a READ_ONLY_FIELD_IGNORED warning, so the object a read answers can be sent back as it is.
      - `dropped_account_cache` boolean: Present on the write and true: updating a calculated metric empties every cached result of the account, reports and Data Studio alike, once the new definition has been stored. A dry run does not carry it, because a dry run empties nothing; the DROPS_ACCOUNT_CACHE warning says what a real call would do.
      - `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.
  - `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)
