---
title: "Get integration details"
method: POST
path: "/integrations/detail"
tags: ["Integrations"]
---

# Get integration details

`POST /integrations/detail`

## Request

- Base URL: `https://api.reportingninja.com/v1`
- URL: `https://api.reportingninja.com/v1/integrations/detail`
- Auth: HTTP bearer

Returns the full configuration for one integration: data views (if any), settings (e.g. attribution_window for facebook_ads) and the filter catalog (fields and allowed operators per field).

## Request body

- IntegrationDetailRequest
  - `integration_id` string, required: Public integration id (e.g. facebook_ads).

## Response `200`

The answer of the call, or a business error envelope. This endpoint reports its own errors with HTTP 200 and status=error: read envelope.status before reading data.

- union
  - IntegrationDetailResponse
    - `status` 'ok', required
    - `data` IntegrationDetailData, required: An array with the one integration that was asked for.
      - `integrations` Integration[], required
        - `id` string, required
        - `name` string, required
        - `category` string, required
        - `supports_custom_fields_per_account` boolean, required
        - `notes` string[]: Integration-wide advisories that do not fit in filters/settings/data_views (e.g. data freshness windows, upstream API quotas, billing notes). Optional: only present when there are global notes to publish. Each entry is a short, audience-API sentence; the array is unordered and clients should treat it as a bag of standalone notes.
        - `data_views` DataView[], nullable: The data views of the integration, or null when it has none (facebook_ads, ga4, google_search_console, twitter_analytics and cross_platform).
          - `id` string, required
          - `name` string, required
          - `description` string: Public-facing summary explaining what this data_view returns. Short (1-2 sentence) hint for the API consumer about the underlying scope/endpoint and notable restrictions; does NOT replace the integration-level `filters.note` which covers global concerns (settings, prefix, custom fields, etc.). Always present in real responses (iter 24+); omitted from compact examples in this spec for readability but emitted by /v1/integrations/detail for every data_view.
          - `type` 'campaign' | 'list': Mailchimp-only: indicates which entity (campaign|list) this data_view affines with. The API validates that data_view.type matches settings.entity for mailchimp queries. Not present for other integrations.
        - `settings` Setting[], nullable: The settings the integration reads, or null when it reads none.
          - `id` string, required
          - `name` string, required
          - `description` string
          - `required` boolean: When true, the setting must always be sent in /v1/query. Only one setting is declared required without a recommended_value: mailchimp.entity.
          - `dynamic` boolean: When true, the valid values are account-specific (discover via /v1/connections). The API does not validate the value against a closed list, but the setting is still REQUIRED when its applies_to_data_views matches.
          - `multi_select` boolean: When true, the value is a JSON array of strings (each validated against `options[]`).
          - `note` string
          - `applies_to_data_views` string[]: When present, the setting is REQUIRED only when the current data_view is in this list. When absent, the setting applies to every data_view of the integration.
          - `recommended_value` union: A value the upstream native API would typically apply when this setting is not explicitly customized. PROVIDED ONLY AS A RECOMMENDATION for clients that do not have a specific preference. The API does NOT inject this value automatically: the client must always include the setting in /v1/query when it applies (per `applies_to_data_views`). Reporting Ninja does not define the recommended_value; it mirrors the upstream API's documented default behavior.
            - string
            - string[]
          - `options` object[]
            - `value` string, required
            - `label` string, required
          - `options_by_data_view` object: Per-data_view options map. Used when the valid values of a setting depend on the current data_view (e.g. pinterest_organic.pinterest_sort_by: 5 values for top_pins, 9 for top_video_pins).
        - `filters` FiltersDescriptor: Describes the filter capabilities of an integration. Two shapes coexist: (a) integrations with a closed catalog of filter fields (e.g. facebook_ads, google_ads) expose `fields[]` with the operators valid for each filter field; (b) integrations with dynamic filter fields (e.g. ga4) expose `note`, `dimension_operators` and `metric_operators` and require the client to send `type_of_field` per filter.
          - `supported` boolean, required
          - `combination` 'and' | 'or' | 'and_or': How filters can be combined. `and` (facebook_ads, google_ads, microsoft_ads): all filters are AND-ed; the client must NOT send `combination` per filter (rejected for microsoft_ads). `and_or` (ga4): each filter may include `combination` (`AND` default, or `OR`); see ga4 notes for the iter 3 caveat about the second filter defining the group combination.
          - `fields` FilterField[]: Present for integrations with a closed catalog of filter fields.
            - `id` string, required
            - `name` string, required
            - `category` string, required
            - `operators` string[], required
            - `allowed_values` string[]: Closed list of valid filter values (case-sensitive). Present only for fields with a fixed enum (e.g. microsoft_ads CampaignStatus / AdDistribution / DeviceType). When present, every value sent in /v1/query.filters[].value MUST belong to this list (each element if value is an array).
            - `data_views` string[]: List of data_view ids where this filter field is valid. Present for integrations where filter fields depend on the data_view (e.g. microsoft_ads). If absent, the field is valid for ALL data_views of the integration.
            - `aliases` string[]: Other ids accepted for this same filter field, typically the id the field has in /v1/fields (e.g. facebook_ads `campaign_name` for the filter field `campaign.name`). Any of them may be sent as filters[].field and behaves exactly like the canonical id; `id` remains the canonical one and the one returned in this catalog. Present only for fields that have synonyms.
            - `type_of_field` string: Whether the field is a dimension or a metric. Present for the integrations that ask the client to send type_of_field with each filter, and for the ones whose catalog states it field by field.
          - `note` string: Free-text guidance for the client: how filters can be combined, per-field caveats, matching rules for text operators, or the reason filters are not supported. Present for most integrations, and always worth reading before building a filter.
          - `dimension_operators` string[]: Operators valid for dimension filters. Present for integrations with dynamic filter fields.
          - `metric_operators` string[]: Operators valid for metric filters. Present for integrations with dynamic filter fields.
        - `queryable` boolean, required: Whether /v1/query and /v1/query/async accept this integration. When false, everything else is available -- the integration can be connected, its fields and filters can be read and its widgets can be created -- and only reading its rows through the query endpoints is refused.
        - `not_queryable_reason` string: Why the data of this integration cannot be read through the query endpoints. Present only when queryable is false.
    - `meta` Meta, required
      - `request_id` string, required: Id of this request, useful when reporting an incident.
      - `timestamp` string, date-time, required: When the answer was built, in UTC.
  - ErrorEnvelope
    - `status` 'error', required
    - `error_code` 'AUTH_INVALID' | 'AUTH_EXPIRED' | 'RATE_LIMITED' | 'WRITE_ACCESS_DISABLED' | 'WRITE_TEMPORARILY_UNAVAILABLE' | 'NOT_FOUND' | 'ENDPOINT_NOT_FOUND' | 'INVALID_REQUEST' | 'INVALID_MODE' | 'PAYLOAD_TOO_LARGE' | 'MISSING_REQUIRED_FIELD' | 'INVALID_VALUE' | 'INVALID_LIMIT' | 'UNKNOWN_PARAM' | 'INTEGRATION_NOT_SUPPORTED' | 'INTEGRATION_NOT_IMPLEMENTED' | 'INTEGRATION_NOT_QUERYABLE' | 'FIELD_NOT_AVAILABLE' | 'FILTER_NOT_WIRED' | 'INVALID_FILTER' | 'INVALID_DATE_RANGE' | 'CONNECTION_NOT_FOUND' | 'PLAN_LIMIT_REACHED' | 'PROVIDER_PERMISSION' | 'PROVIDER_UNAVAILABLE' | 'JOB_NOT_FOUND' | 'CURSOR_EXPIRED' | 'VALIDATION_FAILED' | 'APPEND_NOT_ALLOWED' | 'TEMPLATE_NOT_FOUND' | 'CONFIRMATION_EXPIRED' | 'CONFIRMATION_MISMATCH' | 'IDEMPOTENCY_CONFLICT' | 'IDEMPOTENCY_IN_PROGRESS' | 'VERSION_MISMATCH' | 'SECTION_BUSY' | 'REPORT_IN_USE' | 'NOT_ALLOWED' | 'IN_USE' | 'REPORT_IN_TRASH' | 'REPORT_NOT_IN_TRASH' | 'WIDGET_EXECUTION_FAILED' | 'ROW_RULE' | 'POSITION_CHANGED' | 'SECTION_CHANGED' | 'AI_SUMMARY_NOT_ALLOWED' | 'DATA_VIEW_CHANGED' | 'WIDGET_CHANGED' | 'WIDGET_NOT_EDITABLE' | 'SCHEDULE_ALREADY_EXISTS' | 'ALREADY_QUEUED' | 'SCHEDULE_ALREADY_QUEUED' | 'SCHEDULE_QUEUED' | 'SCHEDULE_USES_SYSTEM_TEMPLATE' | 'NOT_EDITABLE_BY_API' | 'PORTAL_NOT_CONFIGURED' | 'USER_EMAIL_EXISTS' | 'ACCOUNT_HAS_NO_OWNER' | 'EMAIL_QUOTA_REACHED' | 'NOTIFICATION_FAILED' | 'INTERNAL', required: Machine-readable code that classifies the error. Stable across versions. Clients should switch on this code instead of parsing the `message` text. Reading the HTTP status alone is not enough: the data endpoints answer their business errors with HTTP 200 and status=error, while the report endpoints answer with the real status.
    - `message` string, required: Human-readable error description. Useful for logs and UIs; not stable across versions -- use `error_code` for programmatic handling.
    - `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.
    - `details` object: Machine-readable detail of the refusal, present only on the errors that have one. Four shapes are published today. VALIDATION_FAILED and UNKNOWN_PARAM carry `problems`, an array of `Problem` naming the path, the code and the message of each thing that is wrong, with `suggestions` when a closed list exists; a body wrong in more than 50 places gets the first 50 and a last entry with code TOO_MANY_PROBLEMS. VERSION_MISMATCH carries `expected` and `actual`. REPORT_IN_USE carries `portal_views` and `reports`, each an array of {key, name} that must be detached first, and an entry of `reports` that is itself in the trash adds `in_trash: true`. REPORT_IN_TRASH carries `trashed_at`, `restorable_until` and `restore`. APPEND_NOT_ALLOWED and PORTAL_NOT_CONFIGURED carry `reason`, the INVALID_VALUE of a widget placement carries `first_free_row`, WIDGET_EXECUTION_FAILED carries the `verdict`, `message` and `reason` of the run, and ALREADY_QUEUED carries `schedule_key` and `report_key`: the one-off request of the snapshot that is still waiting, and the report it was asked of. IN_USE carries `used_by`, naming everything of the account that still holds the row a deletion was asked of, in the shape of that entity: an `EntityUsage` for a saved filter and a calculated metric, an `EntityUsageReports` for a language file and an `EntityUsageSchedules` for an email template. The IN_USE of the email template the client portal sends its notifications with carries `reason` `client_portal` as well, and it is the only IN_USE that carries a reason. NOT_ALLOWED carries `reason`, the rule that stops the write (`system_defined` for a row the platform ships and keeps).
      - `problems` Problem[]
        - `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.
      - `used_by` union: What still reads the definition the call tried to delete. The families depend on the entity: a saved filter or a calculated metric is asked about reports, filtered metrics, portal views and Data Studio account groups; an email template about schedules and the client portal; a language file about reports.
        - EntityUsage: What still reads a saved filter or a calculated metric, published inside details.used_by of an IN_USE. Only the families the question covers are present: a filter is asked about reports, calculated metrics, portal views, alerts and Data Studio account groups, and carries the five; a calculated metric is asked about reports and account groups, and carries those two alone rather than empty lists nobody looked into.
          - `reports` EntityUsageReport[], required
            - `report_key` string, nullable, required: The report the widgets and sections belong to. It can be the key of a TEMPLATE, because a widget of a template based report belongs to the template; and it is null when the report of the element could not be resolved, in which case the entry stands for a single widget or section.
            - `internal_name` string, nullable, required: What the report is called inside the account. Null when the key names a template or a report that is not there any more.
            - `in_trash` boolean, required: Whether the report is in the trash. A report in the trash still reads the definition.
            - `widgets` integer, required: How many widgets of it read the definition.
            - `sections` integer, required: How many sections of it read the definition as a global filter.
          - `calculated_metrics` EntityUsageEntity[]: Only for a saved filter: the filtered metrics built on it.
            - `calculated_metric_key` string: Present in calculated_metrics.
            - `portal_view_key` string: Present in portal_views.
            - `name` string, nullable: What it is called.
          - `portal_views` EntityUsageEntity[]: Only for a saved filter: the portal views that hand it to their clients.
            - `calculated_metric_key` string: Present in calculated_metrics.
            - `portal_view_key` string: Present in portal_views.
            - `name` string, nullable: What it is called.
          - `alerts` integer: Only for a saved filter: how many alerts of the account are evaluated with it.
          - `account_groups` integer, required: How many Data Studio account groups read it.
        - EntityUsageSchedules: What still sends an email template, published inside details.used_by of an IN_USE.
          - `schedules` EntityUsageSchedule[], required
            - `schedule_key` string, required: The delivery, as /v1/schedules/list names it.
            - `report_key` string, nullable, required: The report it delivers.
            - `frequency` string, nullable, required: How often it goes out.
          - `client_portal` boolean, required: Whether the client portal of the account sends its notifications with this template, which is chosen in the application.
        - EntityUsageReports: What still reads a language file, published inside details.used_by of an IN_USE.
          - `reports` EntityUsageReportRef[], required
            - `report_key` string, required
            - `internal_name` string, nullable, required: What the report is called inside the account.
            - `in_trash` boolean, required: Whether the report is in the trash. A report in the trash still points at the file and would come back pointing at nothing.
      - `reason` string, nullable: The rule behind the refusal, where it has a name: the system_defined of a NOT_ALLOWED, the template_based of an APPEND_NOT_ALLOWED, the reason a widget could not be run. Null when the refusal has no named rule.

## 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.
- `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.

---

[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)
