{
  "openapi": "3.1.0",
  "info": {
    "title": "Infinite Audience API",
    "version": "1.0.0",
    "license": {
      "name": "Proprietary — All Rights Reserved",
      "url": "https://infiniteaudience.ai/terms"
    },
    "description": "The primary REST API for Infinite Audience.\n**Authentication:** All protected endpoints require a Bearer token in the `Authorization` header (`Authorization: Bearer <access_token>`). Obtain an access token by posting your API key to `POST /v1/auth/token`.\n**API Key Scopes:** API keys can be restricted to specific scopes. If a token lacks the required scope for an endpoint, a `403 Forbidden` response is returned with a `SCOPE_REQUIRED` error code. - `discovery` — Read-only access to count, lookup, segments, demographics, catalogs, and status polling. - `purchase` — Read-write access to create/update segments and audiences, link campaigns, and execute purchases/deliveries. - `account` — Full access to self-service API key management, webhook settings, delivery destinations, and billing details.\n"
  },
  "servers": [
    {
      "url": "https://api.infiniteaudience.ai",
      "description": "Production API Server"
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Bearer access token obtained from POST /v1/auth/token. Include in all protected requests as: Authorization: Bearer <access_token>\n"
      }
    },
    "schemas": {
      "CostEstimate": {
        "type": "object",
        "required": [
          "base_cost",
          "match_cost",
          "field_cost",
          "destination_cost",
          "total_cost",
          "unit_price",
          "billing_count"
        ],
        "properties": {
          "base_cost": {
            "type": "number",
            "description": "Base audience-delivery cost."
          },
          "match_cost": {
            "type": "number",
            "description": "One-time deferred file-match cost included only until the underlying run is first delivered."
          },
          "field_cost": {
            "type": "number",
            "description": "Total surcharge for requested attributes."
          },
          "destination_cost": {
            "type": "number",
            "description": "Destination surcharge (0 for download)."
          },
          "total_cost": {
            "type": "number",
            "description": "Sum of all cost components."
          },
          "unit_price": {
            "type": "number",
            "description": "Effective cost per record."
          },
          "billing_count": {
            "type": "integer",
            "description": "Number of records that will be billed."
          }
        }
      },
      "LicensePricing": {
        "type": "object",
        "required": [
          "status",
          "evaluated_at",
          "already_licensed_count"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "live",
              "cached",
              "unavailable"
            ],
            "description": "Whether cost_estimate reflects a freshly-computed license split (live), a recent cached one (cached, still authoritative — only the underlying counts are reused, dollars are always recomputed from current rates), or could not be determined in time (unavailable — cost_estimate equals max_cost_estimate exactly in this case, so no consumer sees a misleading number).\n"
          },
          "evaluated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the license split backing cost_estimate was computed. For a cached split this is the ORIGINAL computation time, not now — use it to judge staleness. Null when status is unavailable.\n"
          },
          "already_licensed_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "How many of this audience's candidate identities already hold an active license elsewhere and were therefore excluded from cost_estimate's billable count. Null when status is unavailable.\n"
          }
        }
      },
      "IntegrationRunCounts": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "input",
          "matched",
          "delivered",
          "applied",
          "unchanged",
          "skipped",
          "failed"
        ],
        "properties": {
          "input": {
            "type": "integer",
            "minimum": 0
          },
          "matched": {
            "type": "integer",
            "minimum": 0
          },
          "delivered": {
            "type": "integer",
            "minimum": 0
          },
          "applied": {
            "type": "integer",
            "minimum": 0
          },
          "unchanged": {
            "type": "integer",
            "minimum": 0
          },
          "skipped": {
            "type": "integer",
            "minimum": 0
          },
          "failed": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "IntegrationRun": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "run_id",
          "provider",
          "connection_id",
          "connection_display_name",
          "kind",
          "data_flow",
          "purpose",
          "status",
          "billing_source",
          "customer_boundary",
          "counts",
          "selected_fields",
          "boundary_at",
          "failure_code",
          "failure_message",
          "created_at",
          "updated_at",
          "completed_at"
        ],
        "properties": {
          "run_id": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "connection_id": {
            "type": "string"
          },
          "connection_display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "kind": {
            "type": "string",
            "enum": [
              "batch_enrichment",
              "file_enrichment",
              "ingest",
              "activation",
              "export"
            ]
          },
          "data_flow": {
            "type": "string",
            "enum": [
              "provider_to_platform",
              "platform_to_provider",
              "round_trip"
            ]
          },
          "purpose": {
            "type": "string",
            "enum": [
              "enrichment",
              "ingest",
              "activation",
              "export"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "processing",
              "awaiting_boundary",
              "settling",
              "completed",
              "failed",
              "cancelled"
            ]
          },
          "billing_source": {
            "type": "string",
            "enum": [
              "native",
              "shopify"
            ]
          },
          "customer_boundary": {
            "type": "string",
            "enum": [
              "api_response",
              "file_available",
              "destination_handoff",
              "provider_writeback"
            ]
          },
          "counts": {
            "$ref": "#/components/schemas/IntegrationRunCounts"
          },
          "selected_fields": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "boundary_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "failure_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "failure_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "IntegrationRunListResponse": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "runs",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "runs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IntegrationRun"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "IntegrationMatchCollection": {
        "type": "object",
        "required": [
          "collection_id",
          "org_id",
          "name",
          "kind",
          "status",
          "retention_days",
          "unique_identity_count",
          "count_refreshed_at",
          "last_captured_at",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "collection_id": {
            "type": "string"
          },
          "org_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "default"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "paused"
            ]
          },
          "retention_days": {
            "type": "integer",
            "enum": [
              90
            ]
          },
          "unique_identity_count": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "count_refreshed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "last_captured_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IntegrationCollectionFilter": {
        "type": "object",
        "required": [],
        "additionalProperties": false,
        "properties": {
          "providers": {
            "type": "array",
            "maxItems": 20,
            "items": {
              "type": "string"
            }
          },
          "connection_ids": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "string"
            }
          },
          "integration_run_ids": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "string"
            }
          },
          "source_kinds": {
            "type": "array",
            "maxItems": 3,
            "items": {
              "type": "string",
              "enum": [
                "recurring",
                "batch",
                "file"
              ]
            }
          },
          "boundary_at_from": {
            "type": "string",
            "format": "date-time"
          },
          "boundary_at_to": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IntegrationMaterializationPreview": {
        "type": "object",
        "required": [
          "source_observations",
          "unique_identities",
          "duplicate_observations",
          "expired_identities",
          "suppressed_identities",
          "already_present",
          "net_additions"
        ],
        "properties": {
          "source_observations": {
            "type": "integer",
            "minimum": 0
          },
          "unique_identities": {
            "type": "integer",
            "minimum": 0
          },
          "duplicate_observations": {
            "type": "integer",
            "minimum": 0
          },
          "expired_identities": {
            "type": "integer",
            "minimum": 0
          },
          "suppressed_identities": {
            "type": "integer",
            "minimum": 0
          },
          "already_present": {
            "type": "integer",
            "minimum": 0
          },
          "net_additions": {
            "type": "integer",
            "minimum": 0
          }
        }
      },
      "IntegrationMaterializationPreviewRequest": {
        "type": "object",
        "required": [],
        "additionalProperties": false,
        "properties": {
          "filters": {
            "$ref": "#/components/schemas/IntegrationCollectionFilter"
          },
          "target_segment_id": {
            "type": "string"
          }
        }
      },
      "IntegrationMaterializationPreviewResponse": {
        "type": "object",
        "required": [
          "cutoff_at",
          "preview"
        ],
        "properties": {
          "cutoff_at": {
            "type": "string",
            "format": "date-time"
          },
          "preview": {
            "$ref": "#/components/schemas/IntegrationMaterializationPreview"
          }
        }
      },
      "IntegrationMaterializationRequest": {
        "type": "object",
        "required": [
          "mode"
        ],
        "additionalProperties": false,
        "properties": {
          "mode": {
            "type": "string",
            "enum": [
              "new_segment",
              "add_to_segment"
            ]
          },
          "segment_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 120
          },
          "target_segment_id": {
            "type": "string"
          },
          "filters": {
            "$ref": "#/components/schemas/IntegrationCollectionFilter"
          }
        }
      },
      "IntegrationMaterialization": {
        "type": "object",
        "required": [
          "materialization_id",
          "collection_id",
          "mode",
          "status",
          "filters",
          "source_cutoff_at",
          "target_segment_id",
          "target_segment_name",
          "target_segment_version",
          "preview",
          "identity_count",
          "match_levels",
          "failure_code",
          "failure_message",
          "created_at",
          "updated_at",
          "completed_at"
        ],
        "properties": {
          "materialization_id": {
            "type": "string"
          },
          "collection_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "new_segment",
              "add_to_segment"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "created",
              "processing",
              "completed",
              "failed"
            ]
          },
          "filters": {
            "$ref": "#/components/schemas/IntegrationCollectionFilter"
          },
          "source_cutoff_at": {
            "type": "string",
            "format": "date-time"
          },
          "target_segment_id": {
            "type": "string"
          },
          "target_segment_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "target_segment_version": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1
          },
          "preview": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/IntegrationMaterializationPreview"
              },
              {
                "type": "null"
              }
            ]
          },
          "identity_count": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "match_levels": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "I",
                "H",
                "D",
                "S",
                "A"
              ]
            }
          },
          "failure_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "failure_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "BillingReadinessProjection": {
        "type": "object",
        "required": [
          "requires_card",
          "has_default_card",
          "collection_method_ok",
          "payment_collection_ready",
          "status"
        ],
        "properties": {
          "requires_card": {
            "type": "boolean"
          },
          "has_default_card": {
            "type": "boolean"
          },
          "collection_method_ok": {
            "type": "boolean"
          },
          "payment_collection_ready": {
            "type": "boolean"
          },
          "status": {
            "type": "string",
            "enum": [
              "ok",
              "card_missing",
              "collection_method_mismatch",
              "not_applicable"
            ]
          }
        }
      },
      "BillingUsageRateProjection": {
        "type": "object",
        "required": [
          "match",
          "audience_delivery",
          "destination",
          "enrichment_field"
        ],
        "properties": {
          "match": {
            "type": "object",
            "required": [
              "microbatch",
              "file"
            ],
            "properties": {
              "microbatch": {
                "type": "integer",
                "minimum": 0
              },
              "file": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "audience_delivery": {
            "type": "object",
            "required": [
              "filter",
              "matched",
              "similarity",
              "propensity"
            ],
            "properties": {
              "filter": {
                "type": "integer",
                "minimum": 0
              },
              "matched": {
                "type": "integer",
                "minimum": 0
              },
              "similarity": {
                "type": "integer",
                "minimum": 0
              },
              "propensity": {
                "type": "integer",
                "minimum": 0
              }
            }
          },
          "destination": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          },
          "enrichment_field": {
            "type": "object",
            "additionalProperties": {
              "type": "integer",
              "minimum": 0
            }
          }
        }
      },
      "BillingContractProjection": {
        "type": "object",
        "required": [
          "tier",
          "cadence",
          "commitment_term_months",
          "commitment_amount_cents",
          "usage_discount_bps",
          "contract_start_at",
          "contract_end_at",
          "current_period_start_at",
          "current_period_end_at",
          "pending_change_at",
          "scheduled_change",
          "usage_rates"
        ],
        "properties": {
          "tier": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "paygo",
              "starter",
              "growth",
              "enterprise",
              null
            ]
          },
          "cadence": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "monthly",
              "annual",
              null
            ]
          },
          "commitment_term_months": {
            "type": "integer",
            "enum": [
              0,
              12,
              24,
              36
            ]
          },
          "commitment_amount_cents": {
            "type": "integer",
            "minimum": 0
          },
          "usage_discount_bps": {
            "type": "integer",
            "minimum": 0,
            "maximum": 10000,
            "description": "Contract discount applied to usage rates only; commitment charges and minimums are unchanged."
          },
          "contract_start_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "contract_end_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "current_period_start_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "current_period_end_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "pending_change_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "scheduled_change": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/BillingScheduledContractChange"
              },
              {
                "type": "null"
              }
            ]
          },
          "usage_rates": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/BillingUsageRateProjection"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "BillingScheduledContractChange": {
        "type": "object",
        "required": [
          "kind",
          "request_id",
          "status",
          "effective_at",
          "previous_contract_end_at",
          "successor_contract_id",
          "terms",
          "error_code",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "kind": {
            "type": "string",
            "enum": [
              "replacement",
              "renewal",
              "cancellation"
            ]
          },
          "request_id": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "provisioning",
              "scheduled",
              "failed"
            ]
          },
          "effective_at": {
            "type": "string",
            "format": "date-time"
          },
          "previous_contract_end_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "successor_contract_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "terms": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "tier",
                  "cadence",
                  "commitment_term_months",
                  "commitment_amount_cents",
                  "usage_discount_bps",
                  "usage_rates"
                ],
                "properties": {
                  "tier": {
                    "type": "string",
                    "enum": [
                      "paygo",
                      "starter",
                      "growth",
                      "enterprise"
                    ]
                  },
                  "cadence": {
                    "type": "string",
                    "enum": [
                      "monthly",
                      "annual"
                    ]
                  },
                  "commitment_term_months": {
                    "type": "integer",
                    "enum": [
                      0,
                      12,
                      24,
                      36
                    ]
                  },
                  "commitment_amount_cents": {
                    "type": "integer",
                    "minimum": 0
                  },
                  "usage_discount_bps": {
                    "type": "integer",
                    "minimum": 0,
                    "maximum": 10000
                  },
                  "usage_rates": {
                    "$ref": "#/components/schemas/BillingUsageRateProjection"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "error_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BillingMoneyProjection": {
        "type": "object",
        "required": [
          "available_balance_cents",
          "committed_balance_cents",
          "promotional_balance_cents",
          "current_period_spend_cents",
          "current_invoice_total_cents",
          "next_credit_expiry_at",
          "active_reserved_cents",
          "accepted_pending_cents",
          "effective_available_cents"
        ],
        "properties": {
          "available_balance_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "committed_balance_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "promotional_balance_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "current_period_spend_cents": {
            "type": "integer",
            "minimum": 0
          },
          "current_invoice_total_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "next_credit_expiry_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "active_reserved_cents": {
            "type": "integer",
            "minimum": 0
          },
          "accepted_pending_cents": {
            "type": "integer",
            "minimum": 0
          },
          "effective_available_cents": {
            "type": [
              "integer",
              "null"
            ]
          }
        }
      },
      "BillingSnapshot": {
        "type": "object",
        "required": [
          "billing_account_id",
          "owner_type",
          "owner_id",
          "source",
          "consumer_org_id",
          "billing_authority",
          "agency_id",
          "consumer_postpay_ceiling_cents",
          "consumer_money",
          "billing_model",
          "collection_method",
          "currency",
          "status",
          "contract",
          "money",
          "payment_method_ready",
          "auto_recharge",
          "latest_invoice",
          "collection",
          "projection"
        ],
        "properties": {
          "billing_account_id": {
            "type": "string"
          },
          "owner_type": {
            "type": "string",
            "enum": [
              "org",
              "agency"
            ]
          },
          "owner_id": {
            "type": "string"
          },
          "source": {
            "type": "string"
          },
          "consumer_org_id": {
            "type": "string"
          },
          "billing_authority": {
            "type": "string",
            "enum": [
              "self",
              "agency"
            ]
          },
          "agency_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "consumer_postpay_ceiling_cents": {
            "type": [
              "integer",
              "null"
            ]
          },
          "consumer_money": {
            "type": "object",
            "required": [
              "current_period_spend_cents",
              "active_reserved_cents",
              "accepted_pending_cents",
              "projected_at"
            ],
            "properties": {
              "current_period_spend_cents": {
                "type": "integer",
                "minimum": 0
              },
              "active_reserved_cents": {
                "type": "integer",
                "minimum": 0
              },
              "accepted_pending_cents": {
                "type": "integer",
                "minimum": 0
              },
              "projected_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              }
            }
          },
          "billing_model": {
            "type": "string",
            "enum": [
              "prepay",
              "postpay"
            ]
          },
          "collection_method": {
            "type": "string",
            "enum": [
              "charge_automatically",
              "send_invoice"
            ]
          },
          "currency": {
            "type": "string",
            "const": "usd"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "payment_action_required",
              "past_due",
              "suspended",
              "canceled",
              "provisioning_failed"
            ]
          },
          "contract": {
            "$ref": "#/components/schemas/BillingContractProjection"
          },
          "money": {
            "$ref": "#/components/schemas/BillingMoneyProjection"
          },
          "payment_method_ready": {
            "type": "boolean"
          },
          "auto_recharge": {
            "$ref": "#/components/schemas/BillingAutoRechargeProjection"
          },
          "latest_invoice": {
            "type": "object",
            "required": [
              "id",
              "status"
            ],
            "properties": {
              "id": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "status": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "collection": {
            "$ref": "#/components/schemas/BillingCollectionProjection"
          },
          "projection": {
            "type": "object",
            "required": [
              "projected_at",
              "provider_version"
            ],
            "properties": {
              "projected_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "provider_version": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "BillingCollectionProjection": {
        "type": "object",
        "required": [
          "status",
          "stripe_invoice_id",
          "failure_started_at",
          "grace_period_ends_at",
          "updated_at"
        ],
        "properties": {
          "status": {
            "type": "string",
            "enum": [
              "current",
              "past_due",
              "uncollectible"
            ]
          },
          "stripe_invoice_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "failure_started_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "grace_period_ends_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "BillingAutoRechargeProjection": {
        "type": "object",
        "required": [
          "configured",
          "enabled",
          "threshold_cents",
          "recharge_to_cents",
          "configuration_request_id",
          "payment_status",
          "last_payment_attempt_id",
          "stripe_invoice_id",
          "stripe_payment_intent_id",
          "last_error_code",
          "updated_at",
          "pending_change"
        ],
        "properties": {
          "configured": {
            "type": "boolean"
          },
          "enabled": {
            "type": "boolean"
          },
          "threshold_cents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 500
          },
          "recharge_to_cents": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1500,
            "maximum": 5000000
          },
          "configuration_request_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "payment_status": {
            "type": "string",
            "enum": [
              "none",
              "pending",
              "action_required",
              "paid",
              "failed"
            ]
          },
          "last_payment_attempt_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "stripe_invoice_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "stripe_payment_intent_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "last_error_code": {
            "type": [
              "string",
              "null"
            ]
          },
          "updated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "pending_change": {
            "oneOf": [
              {
                "type": "object",
                "additionalProperties": true,
                "required": [
                  "request_id",
                  "enabled",
                  "threshold_cents",
                  "recharge_to_cents",
                  "status",
                  "last_error_code",
                  "created_at",
                  "updated_at"
                ],
                "properties": {
                  "request_id": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "threshold_cents": {
                    "type": "integer",
                    "minimum": 500
                  },
                  "recharge_to_cents": {
                    "type": "integer",
                    "minimum": 1500,
                    "maximum": 5000000
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "provisioning",
                      "failed"
                    ]
                  },
                  "last_error_code": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "created_at": {
                    "type": "string",
                    "format": "date-time"
                  },
                  "updated_at": {
                    "type": "string",
                    "format": "date-time"
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "AutoRechargeUpdateRequest": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "request_id",
          "enabled",
          "threshold_cents",
          "recharge_to_cents"
        ],
        "properties": {
          "request_id": {
            "type": "string",
            "minLength": 8,
            "maxLength": 160
          },
          "enabled": {
            "type": "boolean"
          },
          "threshold_cents": {
            "type": "integer",
            "minimum": 500,
            "maximum": 4999000
          },
          "recharge_to_cents": {
            "type": "integer",
            "minimum": 1500,
            "maximum": 5000000
          }
        }
      },
      "AccountAutoRechargeResponse": {
        "type": "object",
        "required": [
          "billing_account_id",
          "auto_recharge",
          "action_url"
        ],
        "properties": {
          "billing_account_id": {
            "type": "string"
          },
          "auto_recharge": {
            "$ref": "#/components/schemas/BillingAutoRechargeProjection"
          },
          "action_url": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          }
        }
      },
      "AccountBillingResponse": {
        "type": "object",
        "required": [
          "billing",
          "billing_mode",
          "parent_org",
          "billing_readiness",
          "operational",
          "projection"
        ],
        "properties": {
          "billing": {
            "$ref": "#/components/schemas/BillingSnapshot"
          },
          "billing_mode": {
            "type": "string",
            "enum": [
              "org_billed",
              "parent_billed"
            ]
          },
          "parent_org": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "id",
                  "name"
                ],
                "properties": {
                  "id": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "name": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          },
          "billing_readiness": {
            "$ref": "#/components/schemas/BillingReadinessProjection"
          },
          "operational": {
            "type": "object",
            "required": [
              "frozen",
              "compute_usage_pct"
            ],
            "properties": {
              "frozen": {
                "type": "boolean"
              },
              "compute_usage_pct": {
                "type": "number",
                "minimum": 0
              }
            }
          },
          "projection": {
            "type": "object",
            "required": [
              "projected_at",
              "provider_version",
              "stale",
              "refresh_error_code"
            ],
            "properties": {
              "projected_at": {
                "type": [
                  "string",
                  "null"
                ],
                "format": "date-time"
              },
              "provider_version": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "stale": {
                "type": "boolean"
              },
              "refresh_error_code": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          }
        }
      },
      "BillingCreditBalance": {
        "type": "object",
        "required": [
          "id",
          "kind",
          "name",
          "product_id",
          "product_name",
          "remaining_cents",
          "applicable_product_ids",
          "applicable_product_tags",
          "source",
          "access_schedule",
          "next_expiry_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string",
            "enum": [
              "paid_commit",
              "promotional_credit"
            ]
          },
          "name": {
            "type": "string"
          },
          "product_id": {
            "type": "string"
          },
          "product_name": {
            "type": "string"
          },
          "remaining_cents": {
            "type": "integer",
            "minimum": 0
          },
          "applicable_product_ids": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "applicable_product_tags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "source": {
            "type": [
              "string",
              "null"
            ]
          },
          "access_schedule": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "amount_cents",
                "starting_at",
                "ending_before",
                "active"
              ],
              "properties": {
                "amount_cents": {
                  "type": "integer",
                  "minimum": 0
                },
                "starting_at": {
                  "type": "string",
                  "format": "date-time"
                },
                "ending_before": {
                  "type": "string",
                  "format": "date-time"
                },
                "active": {
                  "type": "boolean"
                }
              }
            }
          },
          "next_expiry_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "AccountCreditsResponse": {
        "type": "object",
        "required": [
          "billing_account_id",
          "balances"
        ],
        "properties": {
          "billing_account_id": {
            "type": "string"
          },
          "balances": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingCreditBalance"
            }
          }
        }
      },
      "BillingInvoice": {
        "type": "object",
        "required": [
          "id",
          "status",
          "type",
          "total_cents",
          "currency",
          "period_start_at",
          "period_end_at",
          "issued_at",
          "line_items",
          "collection"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "type": {
            "type": "string"
          },
          "total_cents": {
            "type": "integer"
          },
          "currency": {
            "type": "string",
            "const": "usd"
          },
          "period_start_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "period_end_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "issued_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "line_items": {
            "type": "array",
            "items": {
              "type": "object",
              "required": [
                "name",
                "type",
                "total_cents",
                "quantity",
                "product_id",
                "pricing_dimensions"
              ],
              "properties": {
                "name": {
                  "type": "string"
                },
                "type": {
                  "type": "string"
                },
                "total_cents": {
                  "type": "integer"
                },
                "quantity": {
                  "type": [
                    "number",
                    "null"
                  ]
                },
                "product_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "pricing_dimensions": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "collection": {
            "oneOf": [
              {
                "type": "object",
                "required": [
                  "provider",
                  "invoice_id",
                  "status",
                  "hosted_invoice_url",
                  "invoice_pdf",
                  "delivery_error"
                ],
                "properties": {
                  "provider": {
                    "type": "string",
                    "const": "stripe"
                  },
                  "invoice_id": {
                    "type": "string"
                  },
                  "status": {
                    "type": [
                      "string",
                      "null"
                    ]
                  },
                  "hosted_invoice_url": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri"
                  },
                  "invoice_pdf": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "format": "uri"
                  },
                  "delivery_error": {
                    "type": [
                      "string",
                      "null"
                    ]
                  }
                }
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "AccountInvoicesResponse": {
        "type": "object",
        "required": [
          "billing_account_id",
          "authority",
          "invoices"
        ],
        "properties": {
          "billing_account_id": {
            "type": "string"
          },
          "authority": {
            "type": "string",
            "enum": [
              "commercial",
              "mixed"
            ]
          },
          "invoices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BillingInvoice"
            }
          }
        }
      },
      "MatchRun": {
        "type": "object",
        "description": "A single micro-batch match run record. `amount_charged` is a conservative pending-usage estimate, not a finalized invoice amount.",
        "required": [
          "run_id",
          "type",
          "status",
          "input_record_count",
          "match_count",
          "match_rate",
          "amount_charged",
          "field_list",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "run_id": {
            "type": "string"
          },
          "type": {
            "type": "string",
            "enum": [
              "microbatch"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "completed",
              "failed"
            ]
          },
          "input_record_count": {
            "type": "integer"
          },
          "match_count": {
            "type": "integer"
          },
          "match_rate": {
            "type": "number",
            "format": "float"
          },
          "amount_charged": {
            "type": "number",
            "format": "float",
            "description": "Conservative USD estimate accepted for billing; not a finalized invoice total."
          },
          "field_list": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "MatchRunList": {
        "type": "object",
        "required": [
          "runs",
          "has_more"
        ],
        "properties": {
          "runs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MatchRun"
            }
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "FileMatchJob": {
        "type": "object",
        "description": "A file-based match job (matched-subtype segment)",
        "required": [
          "match_id",
          "name",
          "status",
          "matching_status",
          "input_record_count",
          "match_count",
          "match_rate",
          "amount_charged",
          "file_format",
          "compression",
          "segment_id",
          "audience_id",
          "audience_status",
          "source_job_id",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "match_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "failed",
              "archived",
              "expired"
            ]
          },
          "matching_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "pending",
              "processing",
              "completed",
              "failed",
              null
            ]
          },
          "input_record_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "match_count": {
            "type": [
              "integer",
              "null"
            ]
          },
          "match_rate": {
            "type": [
              "number",
              "null"
            ],
            "format": "float"
          },
          "amount_charged": {
            "type": [
              "number",
              "null"
            ],
            "description": "One-time file-match USD estimate accepted when this run first participates in a successful delivery. Null before first delivery; excludes the delivery's separate audience/destination/field usage and is not a finalized invoice amount.\n"
          },
          "file_format": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "csv",
              "avro",
              "json",
              "jsonl",
              null
            ]
          },
          "compression": {
            "type": "string",
            "enum": [
              "none",
              "gzip"
            ],
            "description": "Input artifact compression, normalized to none for legacy jobs."
          },
          "column_mappings": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": true
          },
          "error_message": {
            "type": [
              "string",
              "null"
            ]
          },
          "segment_id": {
            "type": "string"
          },
          "audience_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "audience_status": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "active",
              "archived",
              null
            ],
            "description": "Whether the linked audience (if any) is still active in Library. `archived` covers both an explicitly archived audience and a deleted one. `null` when there's no `audience_id`, or its audience document no longer exists. This job's own history is unaffected either way — it persists even after the underlying segment or audience is deleted from Library.\n"
          },
          "source_job_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "integration_run_id": {
            "type": "string",
            "pattern": "^ir_[A-Za-z0-9_-]{40}$",
            "description": "Present only for an authorized private partnership workflow."
          },
          "created_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ]
          }
        }
      },
      "FileMatchJobList": {
        "type": "object",
        "required": [
          "jobs",
          "has_more"
        ],
        "properties": {
          "jobs": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FileMatchJob"
            }
          },
          "has_more": {
            "type": "boolean"
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "additionalProperties": true,
        "properties": {
          "error": {
            "type": "string",
            "description": "Stable machine-readable error code (e.g. `INVALID_STATUS_TRANSITION`, `BILLING_INSUFFICIENT_BALANCE`). Always present.\n"
          },
          "message": {
            "type": "string",
            "description": "Human-readable explanation of the error."
          },
          "code": {
            "type": "string",
            "description": "Alternate machine-readable code — present on some endpoints as an alias for `error` for backward compatibility.\n"
          },
          "request_id": {
            "type": "string",
            "description": "Opaque support/debug identifier when available."
          }
        }
      },
      "OAuthErrorResponse": {
        "type": "object",
        "description": "RFC 6749 §5.2 / §4.1.2.1 error body — see the OAuthError response component.",
        "required": [
          "error",
          "error_description"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "invalid_request",
              "invalid_client",
              "invalid_client_metadata",
              "invalid_grant",
              "invalid_scope",
              "invalid_target",
              "unsupported_response_type",
              "unsupported_grant_type",
              "access_denied",
              "server_error"
            ]
          },
          "error_description": {
            "type": "string"
          }
        }
      },
      "OAuthTokenRequest": {
        "description": "Encoded as `application/x-www-form-urlencoded` or `application/json`. `client_id`/`client_secret` may instead ride on HTTP Basic auth, in which case both fields are omitted from the body.\n",
        "oneOf": [
          {
            "type": "object",
            "required": [
              "grant_type",
              "code",
              "redirect_uri",
              "code_verifier"
            ],
            "properties": {
              "grant_type": {
                "type": "string",
                "const": "authorization_code"
              },
              "code": {
                "type": "string"
              },
              "redirect_uri": {
                "type": "string",
                "format": "uri"
              },
              "code_verifier": {
                "type": "string",
                "description": "RFC 7636 PKCE verifier — must hash (S256) to the code_challenge you sent to /authorize."
              },
              "client_id": {
                "type": "string"
              },
              "client_secret": {
                "type": "string"
              },
              "resource": {
                "type": "string",
                "format": "uri"
              }
            }
          },
          {
            "type": "object",
            "required": [
              "grant_type",
              "refresh_token"
            ],
            "properties": {
              "grant_type": {
                "type": "string",
                "const": "refresh_token"
              },
              "refresh_token": {
                "type": "string"
              },
              "client_id": {
                "type": "string"
              },
              "client_secret": {
                "type": "string"
              },
              "resource": {
                "type": "string",
                "format": "uri"
              }
            }
          }
        ]
      },
      "OAuthTokenResponse": {
        "type": "object",
        "required": [
          "access_token",
          "token_type",
          "expires_in",
          "refresh_token",
          "scope"
        ],
        "properties": {
          "access_token": {
            "type": "string",
            "description": "Opaque bearer token. Use exactly like an API-key-derived one: `Authorization: Bearer <access_token>`."
          },
          "token_type": {
            "type": "string",
            "enum": [
              "Bearer"
            ]
          },
          "expires_in": {
            "type": "integer",
            "description": "Seconds until the access token expires (1800)."
          },
          "refresh_token": {
            "type": "string",
            "description": "Supersedes any previously-issued refresh token for this connection — the old one is now rejected."
          },
          "scope": {
            "type": "string",
            "description": "Space-delimited granted scopes."
          }
        }
      },
      "OAuthRevokeRequest": {
        "type": "object",
        "required": [
          "token"
        ],
        "properties": {
          "token": {
            "type": "string"
          },
          "token_type_hint": {
            "type": "string",
            "enum": [
              "access_token",
              "refresh_token"
            ]
          },
          "client_id": {
            "type": "string"
          },
          "client_secret": {
            "type": "string"
          }
        }
      },
      "OAuthServerMetadata": {
        "type": "object",
        "required": [
          "issuer",
          "authorization_endpoint",
          "token_endpoint",
          "revocation_endpoint",
          "registration_endpoint",
          "response_types_supported",
          "grant_types_supported",
          "code_challenge_methods_supported",
          "token_endpoint_auth_methods_supported",
          "client_id_metadata_document_supported",
          "resource_indicators_supported",
          "scopes_supported"
        ],
        "properties": {
          "issuer": {
            "type": "string",
            "format": "uri"
          },
          "authorization_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "token_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "revocation_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "registration_endpoint": {
            "type": "string",
            "format": "uri"
          },
          "response_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "grant_types_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "code_challenge_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "token_endpoint_auth_methods_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "client_id_metadata_document_supported": {
            "type": "boolean"
          },
          "resource_indicators_supported": {
            "type": "boolean"
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "OAuthClientRegistrationRequest": {
        "type": "object",
        "required": [
          "redirect_uris"
        ],
        "properties": {
          "client_name": {
            "type": "string"
          },
          "redirect_uris": {
            "type": "array",
            "minItems": 1,
            "maxItems": 20,
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "scope": {
            "type": "string"
          },
          "token_endpoint_auth_method": {
            "type": "string",
            "enum": [
              "none"
            ]
          },
          "grant_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "authorization_code",
                "refresh_token"
              ]
            }
          },
          "response_types": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "code"
              ]
            }
          }
        }
      },
      "OAuthClientRegistrationResponse": {
        "type": "object",
        "required": [
          "client_id",
          "client_id_issued_at",
          "client_name",
          "redirect_uris",
          "scope",
          "token_endpoint_auth_method",
          "grant_types",
          "response_types"
        ],
        "properties": {
          "client_id": {
            "type": "string"
          },
          "client_id_issued_at": {
            "type": "integer"
          },
          "client_name": {
            "type": "string"
          },
          "redirect_uris": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "scope": {
            "type": "string"
          },
          "token_endpoint_auth_method": {
            "type": "string",
            "enum": [
              "none"
            ]
          },
          "grant_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "response_types": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "OAuthProtectedResourceMetadata": {
        "type": "object",
        "required": [
          "resource",
          "authorization_servers",
          "bearer_methods_supported",
          "scopes_supported",
          "resource_documentation"
        ],
        "properties": {
          "resource": {
            "type": "string",
            "format": "uri"
          },
          "authorization_servers": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uri"
            }
          },
          "bearer_methods_supported": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "header"
              ]
            }
          },
          "scopes_supported": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "resource_documentation": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "NotifiableEventType": {
        "type": "string",
        "description": "Events that can trigger a webhook delivery. Segment and delivery events support ordinary durable subscriptions and the org's legacy webhook. Partnership events are restricted to the owning OAuth grant and are never sent to an org-wide webhook.\n",
        "enum": [
          "segment.ready",
          "segment.failed",
          "delivery.completed",
          "delivery.failed",
          "integration.enrichment.prepared",
          "integration.enrichment.failed",
          "integration.file.ready",
          "integration.file.failed"
        ]
      },
      "WebhookSubscription": {
        "type": "object",
        "description": "A durable event-trigger subscription (e.g. a Zap that is turned on). Delivery is retried with exponential backoff; see the Triggers tag for the ownership model.\n",
        "required": [
          "id",
          "target_url",
          "events",
          "status",
          "created_at",
          "created_by",
          "last_delivery_at",
          "consecutive_failure_count"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "target_url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotifiableEventType"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "disabled",
              "revoked"
            ],
            "description": "\"disabled\" is set automatically after 20 consecutive dead-lettered deliveries; \"revoked\" is explicit — owner action, disconnecting the partnership, or the target endpoint itself returning 410 Gone.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "created_by": {
            "type": "string",
            "description": "Identifier of the creating caller."
          },
          "last_delivery_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "consecutive_failure_count": {
            "type": "integer",
            "description": "Resets to 0 on any successful delivery; reaching 20 auto-disables the subscription."
          }
        }
      },
      "WebhookSubscriptionCreated": {
        "type": "object",
        "description": "Response to POST /v1/webhook-subscriptions. `secret` is returned exactly once, in plaintext — it is never retrievable again after this response.\n",
        "required": [
          "id",
          "target_url",
          "events",
          "secret",
          "status",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string"
          },
          "target_url": {
            "type": "string",
            "format": "uri"
          },
          "events": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NotifiableEventType"
            }
          },
          "secret": {
            "type": "string",
            "description": "Plaintext HMAC-SHA256 signing secret. Store it now — every subsequent read only returns it masked. Used to compute `X-IA-Signature` on every delivery to this subscription — see the Webhooks tag's Signing section for the exact scheme.\n"
          },
          "status": {
            "type": "string",
            "enum": [
              "active"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IntegrationAssetProvenance": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "source_provider",
          "source_connection_id",
          "source_connection_display_name",
          "source_run_id",
          "source_boundary_at",
          "promoted_at"
        ],
        "properties": {
          "source_provider": {
            "type": "string"
          },
          "source_connection_id": {
            "type": "string"
          },
          "source_connection_display_name": {
            "type": [
              "string",
              "null"
            ]
          },
          "source_run_id": {
            "type": "string",
            "pattern": "^ir_[A-Za-z0-9_-]{40}$"
          },
          "source_boundary_at": {
            "type": "string",
            "format": "date-time"
          },
          "promoted_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "IntegrationCollectionProvenance": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "version",
          "materialization_id",
          "collection_id",
          "mode",
          "providers",
          "connection_count",
          "source_cutoff_at",
          "source_observations",
          "unique_identities",
          "materialized_at"
        ],
        "properties": {
          "version": {
            "type": "integer",
            "const": 1
          },
          "materialization_id": {
            "type": "string",
            "pattern": "^imm_[a-f0-9]{40}$"
          },
          "collection_id": {
            "type": "string"
          },
          "mode": {
            "type": "string",
            "enum": [
              "new_segment",
              "add_to_segment"
            ]
          },
          "providers": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "connection_count": {
            "type": "integer",
            "minimum": 0
          },
          "source_cutoff_at": {
            "type": "string",
            "format": "date-time"
          },
          "source_observations": {
            "type": "integer",
            "minimum": 0
          },
          "unique_identities": {
            "type": "integer",
            "minimum": 0
          },
          "materialized_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "ExpandedSegment": {
        "type": "object",
        "description": "Lightweight summary of a constituent segment, expanded server-side from the audience's composition. Returned on both list and single-GET audience responses. `subtype` is the segment's own subtype — use the first entry of `segments` (the primary included segment) to derive subtype-specific presentation for the audience.\n",
        "required": [
          "id",
          "name",
          "record_count",
          "subtype",
          "input_record_count",
          "match_count"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Segment document ID."
          },
          "name": {
            "type": "string",
            "description": "Segment display name."
          },
          "record_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Cached record count of the segment. Null if never counted."
          },
          "subtype": {
            "type": "string",
            "enum": [
              "filter",
              "matched",
              "similarity",
              "propensity"
            ],
            "description": "Subtype of the constituent segment."
          },
          "input_record_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "matched subtype only. Count of records in the uploaded identity file. Null otherwise."
          },
          "match_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "matched subtype only. Count of records successfully matched. Null otherwise."
          }
        }
      },
      "AudienceObject": {
        "type": "object",
        "required": [
          "audience_id",
          "name",
          "status",
          "version",
          "expires_at",
          "visibility",
          "segment_refs",
          "segments",
          "excluded_segments",
          "integration_provenance",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "integration_provenance": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/IntegrationAssetProvenance"
              },
              {
                "type": "null"
              }
            ],
            "description": "Source partnership and boundary for a result saved from Connect; null otherwise."
          },
          "audience_id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived",
              "expired"
            ],
            "description": "active = ready for delivery; archived = soft-deleted via PATCH status:archived (excluded from list by default); expired = past 90-day TTL. Note: pending/failed statuses belong on segments, not audiences.\n"
          },
          "version": {
            "type": "integer"
          },
          "quote_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID of the most recent delivery quote for this audience."
          },
          "quote_total_cents": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Total cost in cents from the most recent delivery quote."
          },
          "parent_audience_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID of the audience this was forked from (null if not a fork)."
          },
          "forked_from_snapshot_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Snapshot version of the parent audience at the time of forking."
          },
          "linked_campaign_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID of the campaign this audience is linked to, if any."
          },
          "record_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Total resolved records across all composed segments."
          },
          "matched_record_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Records contributed by matched-subtype segments in this audience's composition. Present only when the composition includes at least one included matched segment; null for pure-filter or non-matched compositions.\n"
          },
          "match_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Records resolved to a known identity, for an audience created via the direct file-upload flow (POST /v1/match/file). Populated when that upload's matching pipeline completes; null before then or for audiences not created that way — use matched_record_count for a composition-level matched count instead.\n"
          },
          "input_record_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Rows in the originally uploaded identity file, for an audience created via POST /v1/match/file. Same population conditions as match_count.\n"
          },
          "match_rate": {
            "type": [
              "number",
              "null"
            ],
            "description": "match_count / input_record_count, rounded to 4 decimal places. Null unless both match_count and input_record_count are present.\n"
          },
          "expires_at": {
            "type": "string",
            "description": "ISO date — 90-day hard expiry from creation"
          },
          "version_created_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "visibility": {
            "type": "string",
            "enum": [
              "org",
              "private"
            ],
            "description": "org (default) — visible to all members of the org. private — visible only to the creating user.\n"
          },
          "segment_refs": {
            "type": "array",
            "description": "Composition of segments that define this audience. Each entry specifies a segment and its role (include or exclude) in the set operation.\n",
            "items": {
              "type": "object",
              "required": [
                "segment_id",
                "role"
              ],
              "properties": {
                "segment_id": {
                  "type": "string",
                  "description": "ID of the referenced segment."
                },
                "role": {
                  "type": "string",
                  "enum": [
                    "include",
                    "exclude"
                  ],
                  "description": "Whether the segment's records are included in or excluded from the audience."
                }
              }
            }
          },
          "segment_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Cache of include-role segment IDs (denormalized from segment_refs for query efficiency)."
          },
          "excluded_segment_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Cache of exclude-role segment IDs (denormalized from segment_refs for query efficiency)."
          },
          "segments": {
            "type": "array",
            "description": "Expanded summaries of the include-role segments, in `segment_ids` order. Populated on both list and single-GET responses. Best-effort — missing or deleted segment docs are skipped, so this array may be shorter than `segment_ids`.\n",
            "items": {
              "$ref": "#/components/schemas/ExpandedSegment"
            }
          },
          "excluded_segments": {
            "type": "array",
            "description": "Expanded summaries of the exclude-role segments, in `excluded_segment_ids` order. Same expansion semantics as `segments`.\n",
            "items": {
              "$ref": "#/components/schemas/ExpandedSegment"
            }
          },
          "set_logic": {
            "type": "string",
            "enum": [
              "union",
              "intersection"
            ],
            "description": "How the include-role segments are combined: `union` = a record matches ANY included segment (OR); `intersection` = a record must match EVERY included segment (AND). Segments in `excluded_segment_ids` are always subtracted from that result (AND NOT), regardless of `set_logic`.\n"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DeliveryObject": {
        "type": "object",
        "required": [
          "delivery_id",
          "audience_id",
          "audience_version",
          "destination",
          "status",
          "cost",
          "created_at",
          "expires_at"
        ],
        "properties": {
          "delivery_id": {
            "type": "string"
          },
          "audience_id": {
            "type": "string"
          },
          "audience_version": {
            "type": "integer"
          },
          "destination": {
            "type": "string",
            "enum": [
              "download",
              "liveramp",
              "narrative"
            ]
          },
          "template_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "field_list": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            }
          },
          "status": {
            "type": "string",
            "enum": [
              "processing",
              "completed",
              "failed"
            ]
          },
          "cost": {
            "type": "number",
            "description": "Conservative USD estimate for usage accepted for billing; not a finalized invoice total."
          },
          "estimated_cost": {
            "type": "number",
            "description": "Pre-flight safety ceiling for a still-processing delivery. It is released into the final local `cost` estimate once resolution/export completes; the finalized invoice remains authoritative.\n"
          },
          "already_delivered": {
            "type": "boolean",
            "description": "`true` when this exact audience content, resolved field selection, unmatched-row policy, and destination were already delivered — `cost` is 0 and the existing delivery record is returned.\n"
          },
          "message": {
            "type": "string",
            "description": "Human-readable note, present on free re-delivery responses."
          },
          "created_at": {
            "type": "string"
          },
          "completed_at": {
            "type": [
              "string",
              "null"
            ]
          },
          "expires_at": {
            "type": "string"
          },
          "output_format": {
            "type": "string",
            "enum": [
              "csv",
              "avro",
              "json",
              "jsonl"
            ],
            "description": "Output file format. Present on `download` deliveries. Avro exports preserve native source column types (including `REPEATED`/array columns) rather than flattening everything to strings. Read endpoints normalize legacy records; an immediate DSP-create response may omit this field.\n"
          },
          "output_compression": {
            "type": "string",
            "enum": [
              "none",
              "gzip"
            ],
            "description": "Explicit outer artifact compression, normalized to none for legacy records on read and possibly omitted by an immediate DSP-create response. It never inherits input compression. Avro uses native DEFLATE and therefore reports none here rather than outer gzip.\n"
          },
          "output_bytes": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0,
            "description": "Exact sum of stored object sizes across every output part. Null until completion or when no downloadable artifact is produced.\n"
          },
          "download_urls": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "Signed download URLs for every completed `download` artifact part, sorted deterministically. Always an array and may contain multiple export parts for any format. Signed URLs expire after 24 hours — call `GET /v1/audiences/{id}/deliveries/{did}` to regenerate fresh URLs at any time.\n"
          },
          "output_record_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Actual row count in the exported file. Set at completion for matched audiences only (where the count is unknown until the TVF runs). For filter, similarity, and propensity audiences this equals the audience's `record_count`.\n"
          },
          "failure_reason": {
            "type": [
              "string",
              "null"
            ]
          },
          "matched_segment_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID of the matched (file-match) segment resolved from the delivered audience's composition at delivery creation. Populated only when the audience includes a matched segment; null for filter, similarity, and propensity deliveries and for deliveries created before this attribute existed.\n"
          },
          "source_job_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "The matched segment's originating file-match job ID — links the delivery back to the file-match event that produced its source data. Populated only when the delivered audience resolves a matched segment that carries a source job; null otherwise (including deliveries created before this attribute existed).\n"
          },
          "integration_run_id": {
            "type": "string",
            "pattern": "^ir_[A-Za-z0-9_-]{40}$",
            "description": "Present only for an authorized private partnership workflow."
          }
        }
      },
      "AudienceRefreshResult": {
        "type": "object",
        "description": "Response of `POST /v1/audiences/{id}/refresh`. Deliberately lighter than the full Audience object: the handler returns only the version/lifecycle attributes it changed plus subtype-specific next-step hints. Fetch `GET /v1/audiences/{id}` for the full audience document.\n",
        "required": [
          "audience_id",
          "segment_id",
          "version",
          "version_created_at",
          "expires_at",
          "status",
          "subtype",
          "refreshed",
          "next_step"
        ],
        "properties": {
          "audience_id": {
            "type": "string"
          },
          "segment_id": {
            "type": "string",
            "description": "The caller-chosen member segment that was refreshed (echoed back from the request body). An audience is a composition of N member segments with no primary one — this is always present, identifying which member segment's refresh drove this response.\n"
          },
          "version": {
            "type": "integer",
            "description": "New snapshot version after the refresh."
          },
          "version_created_at": {
            "type": "string",
            "format": "date-time"
          },
          "expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "New 90-day hard expiry."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "pending"
            ],
            "description": "active = the refreshed segment's subtype is filter, refreshed synchronously; pending = matched/similarity/propensity, awaiting re-upload or workflow completion.\n"
          },
          "subtype": {
            "type": "string",
            "enum": [
              "filter",
              "matched",
              "similarity",
              "propensity"
            ],
            "description": "Subtype of the refreshed segment."
          },
          "refreshed": {
            "type": "boolean",
            "description": "true when the refreshed segment was re-derived; false only for the filter-subtype no-op path (segment already active, count already null, TTL already fresh) — this route always calls refreshSegmentCore without force, so that no-op path is reachable here exactly as it is on POST /v1/segments/{id}/refresh.\n"
          },
          "next_step": {
            "type": "string",
            "description": "Human-readable hint for the calling agent on how to proceed (e.g. recount, upload to upload_url, or wait for the workflow).\n"
          },
          "upload_url": {
            "type": "string",
            "description": "Presigned upload URL (24 h expiry). Present only for the matched subtype, when shard_count was 1 (the default) — absent when sharded, use upload_urls instead.\n"
          },
          "upload_urls": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Presigned upload URLs (24 h expiry), one per shard, index-ordered. Present only for the matched subtype when shard_count > 1 was requested — upload_url is absent. Every CSV shard must include the same header row.\n"
          },
          "compression": {
            "type": "string",
            "enum": [
              "none",
              "gzip"
            ],
            "description": "Compression for this matched refresh upload."
          },
          "upload_expires_at": {
            "type": "string",
            "format": "date-time",
            "description": "Expiry for upload_url/upload_urls. Present only for the matched subtype."
          },
          "workflow_run_id": {
            "type": "string",
            "description": "Cloud Workflow execution ID. Present only for similarity and propensity subtypes.\n"
          }
        }
      },
      "SegmentRefreshResult": {
        "description": "Response of `POST /v1/segments/{id}/refresh` — the full updated segment document plus refresh metadata. For the matched subtype the response additionally carries upload_url / upload_expires_at (24 h presign) — or upload_urls (an array, one per shard) when shard_count > 1 was requested, in which case upload_url is absent; for similarity/propensity the segment's workflow_run_id points at the newly launched run.\n",
        "allOf": [
          {
            "$ref": "#/components/schemas/SegmentObject"
          },
          {
            "type": "object",
            "required": [
              "refreshed",
              "affected_audiences",
              "next_step"
            ],
            "properties": {
              "refreshed": {
                "type": "boolean",
                "description": "true when the segment was re-derived; false when the filter no-op path returned the current object unchanged (already active, count already null, TTL already fresh — pass force: true to bump anyway).\n"
              },
              "affected_audiences": {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "description": "IDs of non-deleted audiences that reference this segment. They are NOT auto-refreshed — their status, record_count, and expiry may now be stale. Recount or refresh them as needed, or use POST /v1/audiences/{id}/refresh for a whole-composition refresh.\n"
              },
              "next_step": {
                "type": "string",
                "description": "Human-readable hint for the calling agent on how to proceed (recount, upload to upload_url, or wait for the workflow).\n"
              }
            }
          }
        ]
      },
      "Filter": {
        "type": "object",
        "description": "A single filter condition for audience discovery or job execution",
        "required": [
          "field",
          "op"
        ],
        "properties": {
          "field": {
            "type": "string",
            "description": "The canonical attribute name (e.g. 'age', 'state')"
          },
          "op": {
            "type": "string",
            "enum": [
              "=",
              "!=",
              "<",
              "<=",
              ">",
              ">=",
              "IN",
              "NOT IN",
              "BETWEEN",
              "LIKE",
              "ARRAY_CONTAINS",
              "ARRAY_CONTAINS_ANY",
              "IS NULL",
              "IS NOT NULL"
            ],
            "description": "Comparison operator"
          },
          "value": {
            "oneOf": [
              {
                "type": "string"
              },
              {
                "type": "number"
              },
              {
                "type": "boolean"
              },
              {
                "type": "array",
                "items": {
                  "oneOf": [
                    {
                      "type": "string"
                    },
                    {
                      "type": "number"
                    }
                  ]
                }
              }
            ],
            "description": "The value to match against. Scalar types (`string`, `number`, `boolean`) are used for single-value operators (`=`, `!=`, `<`, `<=`, `>`, `>=`, `LIKE`, `ARRAY_CONTAINS`). Pass an **array** for `IN`, `NOT IN`, and `ARRAY_CONTAINS_ANY` — pass the array directly with no comma-encoding. `BETWEEN` takes a two-element array `[low, high]`. Omit entirely for `IS NULL` / `IS NOT NULL` checks.\n"
          }
        }
      },
      "FilterGroup": {
        "type": "object",
        "description": "A group of Filter conditions. All `filters` within a single group are **always ANDed together** — there is no per-group intra-operator. The `combinator` attribute is an **inter-group** operator: it controls how this group is joined to the immediately preceding group in the array. The `combinator` on the first group (index 0) is always ignored.\n\nExample — `(city = \"NYC\" AND state = \"NY\") OR (state = \"CA\")`:\n```json [\n  { \"id\": \"g1\", \"filters\": [{\"field\":\"city\",\"op\":\"=\",\"value\":\"NYC\"},\n                            {\"field\":\"state\",\"op\":\"=\",\"value\":\"NY\"}],\n    \"combinator\": \"AND\" },\n  { \"id\": \"g2\", \"filters\": [{\"field\":\"state\",\"op\":\"=\",\"value\":\"CA\"}],\n    \"combinator\": \"OR\" }\n] ```\nGroup g1's `combinator` is irrelevant (it is the first group). Group g2's `combinator: \"OR\"` means the result is `(g1) OR (g2)`.\n",
        "required": [
          "id",
          "filters",
          "combinator"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Stable identifier (UUID or NanoID). Used to track groups across edits.\n"
          },
          "filters": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Filter"
            },
            "description": "One or more filter conditions. All conditions in this array are combined with AND. Provide at least one filter per group.\n"
          },
          "combinator": {
            "type": "string",
            "enum": [
              "AND",
              "OR"
            ],
            "description": "**Inter-group operator.** Joins this group to the previous group in the array. `AND` narrows the result; `OR` broadens it. Ignored on the first group (index 0) — that group has no predecessor to join.\n"
          }
        }
      },
      "DiscoveryInput": {
        "type": "object",
        "description": "One side of a discovery operation — provide exactly one of `filters`, `filter_groups`, `segment_id`, `audience_id`, or `segment_ids` + `set_logic`. Providing more than one returns 400 `AMBIGUOUS_INPUT`; providing none returns 400 `NO_INPUT_SPECIFIED`.\n",
        "properties": {
          "filters": {
            "type": "array",
            "description": "Flat filter list.",
            "items": {
              "$ref": "#/components/schemas/Filter"
            }
          },
          "filter_groups": {
            "type": "array",
            "description": "Compound boolean filter groups.",
            "items": {
              "$ref": "#/components/schemas/FilterGroup"
            }
          },
          "segment_id": {
            "type": "string",
            "description": "ID of a saved segment."
          },
          "audience_id": {
            "type": "string",
            "description": "ID of a saved audience."
          },
          "segment_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "IDs of multiple segments to combine (requires `set_logic`)."
          },
          "set_logic": {
            "type": "string",
            "enum": [
              "union",
              "intersection"
            ],
            "description": "How to combine segment_ids: union = match ANY segment (OR); intersection = match EVERY segment (AND).\n"
          }
        }
      },
      "DistributionBucket": {
        "type": "object",
        "description": "One bucket of a categorical distribution.",
        "required": [
          "label",
          "count"
        ],
        "properties": {
          "label": {
            "type": "string"
          },
          "count": {
            "type": "integer"
          }
        }
      },
      "BulkCreateSegmentsResult": {
        "type": "object",
        "description": "Result of POST /v1/segments/bulk-create. Returned with 201 when at least one segment was created, and with 400 (same shape) when every item failed.\n",
        "required": [
          "created",
          "failed",
          "segments",
          "errors"
        ],
        "properties": {
          "created": {
            "type": "integer",
            "description": "Number of segments successfully created."
          },
          "failed": {
            "type": "integer",
            "description": "Number of input items that could not be created."
          },
          "segments": {
            "type": "array",
            "description": "Full segment documents for each successfully created segment.",
            "items": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/SegmentObject"
                },
                {
                  "type": "object",
                  "properties": {
                    "audience_id": {
                      "type": "string",
                      "description": "Auto-created audience wrapper ID. Present only when the input item set create_audience: true.\n"
                    }
                  }
                }
              ]
            }
          },
          "errors": {
            "type": "array",
            "description": "Per-item failures. Empty when all items succeeded.",
            "items": {
              "type": "object",
              "required": [
                "index",
                "name",
                "error"
              ],
              "properties": {
                "index": {
                  "type": "integer",
                  "description": "Zero-based position of the failed item in the input `segments` array."
                },
                "name": {
                  "type": "string",
                  "description": "The `name` from the failed input item."
                },
                "error": {
                  "type": "string",
                  "description": "Human-readable reason the segment could not be created."
                }
              }
            }
          }
        }
      },
      "SegmentObject": {
        "type": "object",
        "required": [
          "segment_id",
          "name",
          "subtype",
          "status",
          "record_count",
          "expires_at",
          "ephemeral",
          "integration_provenance",
          "integration_collection_provenance",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "integration_provenance": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/IntegrationAssetProvenance"
              },
              {
                "type": "null"
              }
            ],
            "description": "Source partnership and boundary for a result saved from Connect; null otherwise."
          },
          "integration_collection_provenance": {
            "oneOf": [
              {
                "$ref": "#/components/schemas/IntegrationCollectionProvenance"
              },
              {
                "type": "null"
              }
            ],
            "description": "Bounded source summary for a segment built from saved integration matches; null otherwise."
          },
          "segment_id": {
            "type": "string",
            "description": "Unique segment identifier"
          },
          "name": {
            "type": "string",
            "description": "Display name of the segment"
          },
          "subtype": {
            "type": "string",
            "enum": [
              "filter",
              "matched",
              "similarity",
              "propensity"
            ],
            "description": "Segment subtype. Determines which additional attributes are present. filter = saved filter criteria; matched = customer list upload with identity resolution; similarity = lookalike model; propensity = ML scoring model.\n"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "failed",
              "archived",
              "expired"
            ],
            "description": "pending = computation in progress (matched/similarity/propensity during creation); active = fully ready; failed = workflow or enrichment error (see error_message); archived = hidden from the default segment catalog and new audience compositions, but otherwise fully functional — audiences that already reference an archived segment are unaffected, and it can be restored with PATCH status:active at any time. Distinct from DELETE, which is not reversible and does change referencing audiences' composition; expired = past 90-day TTL.\n"
          },
          "record_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Total matched/qualified records. Null while pending or if count has not been run."
          },
          "expires_at": {
            "type": "string",
            "description": "Date-only ISO string (90-day hard expiry from creation) for a persistent segment; a full ISO datetime (short, hour-granular TTL) while `ephemeral` is true.\n"
          },
          "ephemeral": {
            "type": "boolean",
            "description": "True for a scratch/free/auto-expiring filter-subtype segment. Absent/false reads as persistent. Promote to persistent via `PATCH /v1/segments/{id} {ephemeral: false}`, or implicitly by composing it into an audience.\n"
          },
          "promoted_at": {
            "type": "string",
            "description": "ISO datetime the segment was promoted from ephemeral to persistent. Absent until promotion happens.\n"
          },
          "promoted_via": {
            "type": "string",
            "enum": [
              "explicit_save",
              "composition"
            ],
            "description": "How the segment was promoted to persistent — `explicit_save` (a direct `ephemeral: false` PATCH) or `composition` (referenced by an audience create/update). Absent until promotion happens.\n"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "org",
              "private"
            ],
            "description": "org (default) — visible to all members of the org. private — visible only to the creating user.\n"
          },
          "current_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "filter subtype only. Current version counter."
          },
          "filters": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/Filter"
            },
            "description": "filter subtype only. Legacy flat filter array."
          },
          "filter_groups": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "$ref": "#/components/schemas/FilterGroup"
            },
            "description": "filter subtype only. Grouped filter conditions."
          },
          "file_format": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "csv",
              "avro",
              "json",
              "jsonl"
            ],
            "description": "matched subtype only. Input file format."
          },
          "compression": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "none",
              "gzip",
              null
            ],
            "description": "matched subtype only. Input artifact compression, normalized to none by current handlers.\n"
          },
          "shard_count": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 1,
            "maximum": 50,
            "description": "matched subtype only. Number of same-format input objects, normalized to 1 for unsharded uploads.\n"
          },
          "column_mappings": {
            "type": [
              "object",
              "null"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "matched subtype only. Source column name → standard identity attribute mappings. Populated after the analyze-and-store workflow gate runs.\n"
          },
          "mapping_confirmed": {
            "type": [
              "boolean",
              "null"
            ],
            "description": "matched subtype only. True once column mappings have been confirmed."
          },
          "input_record_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "matched subtype only. Number of rows in the original upload."
          },
          "match_count": {
            "type": [
              "integer",
              "null"
            ],
            "description": "matched subtype only. Rows that resolved to a known identity after enrichment."
          },
          "match_rate": {
            "type": [
              "number",
              "null"
            ],
            "description": "matched subtype only. match_count / input_record_count."
          },
          "upload_url": {
            "type": [
              "string",
              "null"
            ],
            "description": "matched subtype only. 30-minute signed upload URL. Present on create and refresh responses only, when shard_count was 1 (the default) — absent when sharded, use upload_urls instead.\n"
          },
          "upload_urls": {
            "type": [
              "array",
              "null"
            ],
            "items": {
              "type": "string"
            },
            "description": "matched subtype only. N signed upload URLs (index-ordered). Present on create and refresh responses only when shard_count > 1 was requested — upload_url is absent. Every CSV shard must include the same header row.\n"
          },
          "upload_expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "matched subtype only. Expiry of upload_url/upload_urls."
          },
          "error_message": {
            "type": [
              "string",
              "null"
            ],
            "description": "Present when status is failed. Describes the reason for failure.\n"
          },
          "source_data_segment_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "matched subtype only. Opaque, read-only reference to stored match results when they are shared or versioned independently from the segment metadata. Null when no separate result reference is needed.\n"
          },
          "seed_description": {
            "type": [
              "string",
              "null"
            ],
            "description": "similarity subtype only. Natural language ICP persona description used as seed."
          },
          "seed_segment_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "similarity subtype only. ID of an existing segment used as the lookalike seed."
          },
          "generation_metadata_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "similarity/propensity subtype. References the lookalike run or propensity model ID."
          },
          "workflow_run_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "similarity and propensity subtypes. The workflow run ID. Poll GET /v1/workflows/{workflow_run_id} to monitor progress.\n"
          },
          "positive_class_segment_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "propensity subtype only. ID of the segment whose members are the positive training class."
          },
          "parent_segment_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID of the segment this was duplicated or derived from, if applicable."
          },
          "derived_from": {
            "type": [
              "string",
              "null"
            ],
            "description": "Root ancestor segment ID. Stable across duplicate-of-duplicate chains — always points at the original segment, never an intermediate duplicate.\n"
          },
          "parent_segment_version": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Version of the parent segment pinned at duplicate time (current_version for filter parents; snapshot_version otherwise). Null on segments that are not duplicates.\n"
          },
          "origin_campaign_id": {
            "type": [
              "string",
              "null"
            ],
            "description": "ID of the campaign whose chat session first produced this segment. Metadata only — does not restrict the segment to that campaign. Present only when the segment was created via POST /v1/segments with a campaign_id.\n"
          },
          "usage": {
            "type": "object",
            "description": "Cross-campaign usage stats. Only present on GET /v1/segments/{id} — not included on list responses. Useful for understanding impact before archiving.\n",
            "properties": {
              "audience_count": {
                "type": "integer",
                "description": "Number of active audiences that reference this segment."
              },
              "campaign_count": {
                "type": "integer",
                "description": "Number of distinct campaigns those audiences belong to."
              }
            }
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "DeliveryConfig": {
        "type": "object",
        "description": "Configured delivery destination. The `type` attribute selects the channel; partner destinations require additional identifying attributes.\n",
        "oneOf": [
          {
            "description": "Direct file download — exports are returned through signed, time-limited download URLs.\n",
            "required": [
              "type"
            ],
            "properties": {
              "type": {
                "type": "string",
                "const": "download"
              }
            }
          },
          {
            "description": "Push to a LiveRamp seat for identity resolution and onboarding. `liveramp_seat_id` is required for the routing to work.\n",
            "required": [
              "type",
              "liveramp_seat_id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "const": "liveramp"
              },
              "liveramp_seat_id": {
                "type": "string",
                "description": "LiveRamp seat ID that identifies the target onboarding seat."
              }
            }
          },
          {
            "description": "Push to a Narrative.io dataset for data marketplace distribution. `narrative_dataset_id` is required for the routing to work.\n",
            "required": [
              "type",
              "narrative_dataset_id"
            ],
            "properties": {
              "type": {
                "type": "string",
                "const": "narrative"
              },
              "narrative_dataset_id": {
                "type": "string",
                "description": "Narrative.io dataset ID that identifies the target dataset."
              }
            }
          }
        ]
      },
      "Campaign": {
        "type": "object",
        "required": [
          "id",
          "name",
          "description",
          "status",
          "session_ids",
          "audiences",
          "created_by",
          "updated_by",
          "created_at",
          "updated_at"
        ],
        "description": "A named workspace grouping related audiences and deliveries. There is no audience_ids[] field — audiences[] (below) is the only audience-linkage field the handler ever returns.\n",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique campaign identifier."
          },
          "name": {
            "type": "string",
            "description": "Display name of the campaign."
          },
          "description": {
            "type": "string",
            "maxLength": 1000,
            "description": "User-authored campaign brief, intended market, goals, and constraints."
          },
          "status": {
            "type": "string",
            "enum": [
              "active",
              "archived"
            ],
            "description": "Lifecycle state of the campaign."
          },
          "session_ids": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Associated chat session IDs."
          },
          "audiences": {
            "type": "array",
            "description": "Audiences linked to this campaign — expanded on GET /v1/campaigns/{id}; empty array on list responses.\n",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                },
                "status": {
                  "type": "string",
                  "enum": [
                    "active",
                    "archived",
                    "expired"
                  ]
                },
                "record_count": {
                  "type": [
                    "integer",
                    "null"
                  ]
                },
                "segment_ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "IDs of include-role segments in this audience."
                },
                "excluded_segment_ids": {
                  "type": "array",
                  "items": {
                    "type": "string"
                  },
                  "description": "IDs of exclude-role segments in this audience."
                },
                "segments": {
                  "type": "array",
                  "description": "Expanded summaries of the include-role segments, in `segment_ids` order (best-effort — missing or deleted segment docs are skipped). The first entry is the primary segment; its `subtype` determines the audience's subtype-specific behavior.\n",
                  "items": {
                    "$ref": "#/components/schemas/ExpandedSegment"
                  }
                },
                "excluded_segments": {
                  "type": "array",
                  "description": "Expanded summaries of the exclude-role segments, same semantics as `segments`.",
                  "items": {
                    "$ref": "#/components/schemas/ExpandedSegment"
                  }
                },
                "set_logic": {
                  "type": "string",
                  "enum": [
                    "union",
                    "intersection"
                  ]
                },
                "snapshot_version": {
                  "type": "integer"
                },
                "parent_audience_id": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "linked_at": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "format": "date-time",
                  "description": "When this audience was linked to the campaign."
                },
                "linked_by": {
                  "type": [
                    "string",
                    "null"
                  ],
                  "description": "UID of the user who linked this audience to the campaign."
                }
              }
            }
          },
          "created_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "UID of the user who created the campaign."
          },
          "updated_by": {
            "type": [
              "string",
              "null"
            ],
            "description": "UID of the user who last updated the campaign."
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "OperationEventType": {
        "type": "string",
        "description": "The full set of operational event types the Ops Rail / audit log can record — a superset of the webhook event strings (`segment.ready`, `segment.failed`, `delivery.completed`, `delivery.failed`), which map onto `segment.build.completed`, `segment.build.failed`, `delivery.completed`, and `delivery.failed` respectively.\n",
        "enum": [
          "segment.build.started",
          "segment.build.progress",
          "segment.build.completed",
          "segment.build.failed",
          "segment.created",
          "segment.promoted",
          "segment.refresh.started",
          "segment.refresh.completed",
          "segment.refresh.failed",
          "audience.created",
          "audience.updated",
          "audience.build.failed",
          "delivery.started",
          "delivery.completed",
          "delivery.failed",
          "match.microbatch.completed",
          "integration.enrichment.started",
          "integration.enrichment.prepared",
          "integration.enrichment.completed",
          "integration.enrichment.failed",
          "integration.activation.started",
          "integration.activation.completed",
          "integration.activation.failed",
          "invoice.paid",
          "invoice.collection_failed",
          "invoice.collection_recovered",
          "billing.auto_recharge_updated",
          "billing.provisioning.started",
          "billing.provisioning.progressed",
          "billing.provisioning.completed",
          "billing.provisioning.failed",
          "subscription.tier_changed",
          "subscription.commitment_renewed",
          "subscription.cancellation_scheduled",
          "subscription.cancellation_reverted",
          "subscription.external_plan_activated",
          "subscription.external_plan_suspended",
          "org.provisioned",
          "agency.provisioned"
        ]
      },
      "OperationEvent": {
        "type": "object",
        "description": "A single, immutable status transition in the org's operational event log. Multiple events can share the same `operation_key` — e.g. a matched-segment build emits a `segment.build.started` event and, later, a separate `segment.build.completed` (or `.failed`) event, each with its own `ts`.\n",
        "required": [
          "event_id",
          "org_id",
          "type",
          "status",
          "is_terminal",
          "operation_key",
          "actor_uid",
          "campaign_id",
          "subject",
          "subtype",
          "category",
          "payload",
          "ts"
        ],
        "properties": {
          "event_id": {
            "type": "string"
          },
          "org_id": {
            "type": "string"
          },
          "type": {
            "$ref": "#/components/schemas/OperationEventType"
          },
          "status": {
            "type": "string",
            "description": "The status at this specific timestamp (e.g. `pending`, `processing`, `completed`, `failed`)."
          },
          "is_terminal": {
            "type": "boolean",
            "description": "False means the underlying operation was still in flight as of this event — the Ops Rail renders these with a pulsing marker.\n"
          },
          "operation_key": {
            "type": "string",
            "description": "Stable identifier for the underlying operation (e.g. `segment:seg_123`, `delivery:del_456`) — groups multiple events into one timeline.\n"
          },
          "actor_uid": {
            "type": [
              "string",
              "null"
            ],
            "description": "UID of the user who initiated this specific transition."
          },
          "campaign_id": {
            "type": [
              "string",
              "null"
            ]
          },
          "subject": {
            "type": "object",
            "required": [
              "kind",
              "id",
              "name"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "segment",
                  "audience",
                  "delivery",
                  "match",
                  "invoice",
                  "subscription",
                  "integration",
                  "org",
                  "agency"
                ]
              },
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "subtype": {
            "type": [
              "string",
              "null"
            ],
            "enum": [
              "filter",
              "matched",
              "similarity",
              "propensity",
              null
            ]
          },
          "category": {
            "type": "string",
            "enum": [
              "operational"
            ]
          },
          "payload": {
            "type": "object",
            "additionalProperties": true,
            "description": "Event-type-specific extras (e.g. `progress_pct`, `destination`, `failure_reason`, `record_count`) — shape varies by `type`.\n"
          },
          "ts": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "NotificationPreferences": {
        "type": "object",
        "description": "Per-event-type email opt-in flags for ordinary segment and delivery events. Connection-bound partnership callbacks are automation-only and are not personal email preferences. Absent/false = opt-out (default) — no email until the user explicitly opts in.\n",
        "properties": {
          "segment.ready": {
            "type": "boolean"
          },
          "segment.failed": {
            "type": "boolean"
          },
          "delivery.completed": {
            "type": "boolean"
          },
          "delivery.failed": {
            "type": "boolean"
          }
        },
        "additionalProperties": false
      },
      "NotificationPreferencesResponse": {
        "type": "object",
        "required": [
          "notification_preferences"
        ],
        "properties": {
          "notification_preferences": {
            "type": "object",
            "required": [
              "segment.ready",
              "segment.failed",
              "delivery.completed",
              "delivery.failed"
            ],
            "properties": {
              "segment.ready": {
                "type": "boolean"
              },
              "segment.failed": {
                "type": "boolean"
              },
              "delivery.completed": {
                "type": "boolean"
              },
              "delivery.failed": {
                "type": "boolean"
              }
            }
          }
        }
      },
      "SegmentWebhookReadyPayload": {
        "type": "object",
        "description": "Canonical envelope posted to your webhook when an async segment (matched file upload, similarity, or propensity) completes successfully.\n",
        "required": [
          "id",
          "type",
          "created_at",
          "api_version",
          "org_id",
          "subject",
          "data",
          "links"
        ],
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique per event firing — stable across retries of the same delivery.",
            "example": "evt_1a2b3c4d5e6f7a8b9c0d1e2f"
          },
          "type": {
            "type": "string",
            "enum": [
              "segment.ready"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "type": "string",
            "example": "2026-08-06"
          },
          "org_id": {
            "type": "string"
          },
          "subject": {
            "type": "object",
            "description": "Identifies the segment this event is about.",
            "required": [
              "kind",
              "id",
              "name"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "segment"
                ]
              },
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "data": {
            "type": "object",
            "required": [
              "status"
            ],
            "description": "`status` is always present; the remaining fields vary by segment subtype and creation path — treat any field not listed here as additive.\n",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "completed"
                ]
              },
              "audience_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "If create_audience was true when the segment was created, the ID of the auto-created audience wrapper. Null otherwise.\n"
              },
              "match_count": {
                "type": [
                  "integer",
                  "null"
                ],
                "description": "Present for matched subtype only."
              },
              "record_count": {
                "type": [
                  "integer",
                  "null"
                ]
              }
            },
            "additionalProperties": true
          },
          "links": {
            "type": "object",
            "additionalProperties": true,
            "description": "Reserved for future related-resource links. Currently always empty."
          }
        }
      },
      "SegmentWebhookFailedPayload": {
        "type": "object",
        "description": "Canonical envelope posted to your webhook when an async segment build fails due to an enrichment or workflow error.\n",
        "required": [
          "id",
          "type",
          "created_at",
          "api_version",
          "org_id",
          "subject",
          "data",
          "links"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "evt_1a2b3c4d5e6f7a8b9c0d1e2f"
          },
          "type": {
            "type": "string",
            "enum": [
              "segment.failed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "type": "string",
            "example": "2026-08-06"
          },
          "org_id": {
            "type": "string"
          },
          "subject": {
            "type": "object",
            "description": "Identifies the segment this event is about.",
            "required": [
              "kind",
              "id",
              "name"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "segment"
                ]
              },
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "data": {
            "type": "object",
            "required": [
              "status"
            ],
            "description": "`status` is always present; the remaining fields vary by failure path — treat any field not listed here as additive.\n",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "failed"
                ]
              },
              "failure_reason": {
                "type": "string",
                "description": "Human-readable explanation of the failure."
              },
              "audience_id": {
                "type": [
                  "string",
                  "null"
                ],
                "description": "Audience wrapper ID if one was created alongside the segment."
              }
            },
            "additionalProperties": true
          },
          "links": {
            "type": "object",
            "additionalProperties": true,
            "description": "Reserved for future related-resource links. Currently always empty."
          }
        }
      },
      "DeliveryWebhookCompletedPayload": {
        "type": "object",
        "description": "Canonical envelope posted to your webhook when a delivery export finishes and download URLs (or an equivalent destination confirmation) are available.\n",
        "required": [
          "id",
          "type",
          "created_at",
          "api_version",
          "org_id",
          "subject",
          "data",
          "links"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "evt_1a2b3c4d5e6f7a8b9c0d1e2f"
          },
          "type": {
            "type": "string",
            "enum": [
              "delivery.completed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "type": "string",
            "example": "2026-08-06"
          },
          "org_id": {
            "type": "string"
          },
          "subject": {
            "type": "object",
            "description": "Identifies the delivery this event is about.",
            "required": [
              "kind",
              "id",
              "name"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "delivery"
                ]
              },
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "data": {
            "type": "object",
            "required": [
              "status",
              "destination"
            ],
            "description": "`status` and `destination` are always present; the remaining fields vary by destination and delivery path — treat any field not listed here as additive.\n",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "completed"
                ]
              },
              "destination": {
                "type": "string",
                "enum": [
                  "download",
                  "liveramp",
                  "narrative"
                ]
              },
              "output_record_count": {
                "type": "integer"
              },
              "completed_at": {
                "type": "string",
                "format": "date-time"
              },
              "output_format": {
                "type": "string"
              },
              "output_compression": {
                "type": "string"
              },
              "output_bytes": {
                "type": "integer"
              },
              "download_urls": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uri"
                },
                "description": "Signed download URLs (valid 24 hours). Present for destination=download only."
              }
            },
            "additionalProperties": true
          },
          "links": {
            "type": "object",
            "additionalProperties": true,
            "description": "Reserved for future related-resource links. Currently always empty."
          }
        }
      },
      "DeliveryWebhookFailedPayload": {
        "type": "object",
        "description": "Canonical envelope posted to your webhook when a delivery export fails.\n",
        "required": [
          "id",
          "type",
          "created_at",
          "api_version",
          "org_id",
          "subject",
          "data",
          "links"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "evt_1a2b3c4d5e6f7a8b9c0d1e2f"
          },
          "type": {
            "type": "string",
            "enum": [
              "delivery.failed"
            ]
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "api_version": {
            "type": "string",
            "example": "2026-08-06"
          },
          "org_id": {
            "type": "string"
          },
          "subject": {
            "type": "object",
            "description": "Identifies the delivery this event is about.",
            "required": [
              "kind",
              "id",
              "name"
            ],
            "properties": {
              "kind": {
                "type": "string",
                "enum": [
                  "delivery"
                ]
              },
              "id": {
                "type": "string"
              },
              "name": {
                "type": [
                  "string",
                  "null"
                ]
              }
            }
          },
          "data": {
            "type": "object",
            "required": [
              "status",
              "destination",
              "failure_reason"
            ],
            "description": "`status`, `destination`, and `failure_reason` are always present; the remaining fields vary by delivery path — treat any field not listed here as additive.\n",
            "properties": {
              "status": {
                "type": "string",
                "enum": [
                  "failed"
                ]
              },
              "destination": {
                "type": "string",
                "enum": [
                  "download",
                  "liveramp",
                  "narrative"
                ]
              },
              "failure_reason": {
                "type": "string",
                "description": "Human-readable reason the delivery failed."
              }
            },
            "additionalProperties": true
          },
          "links": {
            "type": "object",
            "additionalProperties": true,
            "description": "Reserved for future related-resource links. Currently always empty."
          }
        }
      },
      "A2AMessagePart": {
        "type": "object",
        "description": "A single content fragment within an A2A message.",
        "required": [
          "type"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "text",
              "data"
            ],
            "description": "Content type. \"text\" for plain text, \"data\" for structured JSON."
          },
          "text": {
            "type": "string",
            "description": "The text content (present when type is \"text\")."
          },
          "data": {
            "type": "object",
            "description": "Structured JSON payload (present when type is \"data\").",
            "additionalProperties": true
          }
        }
      },
      "A2AMessage": {
        "type": "object",
        "description": "A message in an A2A task conversation.",
        "required": [
          "role",
          "parts"
        ],
        "properties": {
          "role": {
            "type": "string",
            "enum": [
              "user",
              "agent"
            ],
            "description": "Who authored this message."
          },
          "parts": {
            "type": "array",
            "description": "One or more content fragments composing the message.",
            "items": {
              "$ref": "#/components/schemas/A2AMessagePart"
            },
            "minItems": 1
          }
        }
      },
      "A2ATask": {
        "type": "object",
        "required": [
          "id",
          "status",
          "messages",
          "created_at",
          "updated_at"
        ],
        "description": "Represents an asynchronous agent task. Submit via POST /a2a/tasks and poll via GET /a2a/tasks/{id} until status reaches a terminal state.",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "description": "Unique task identifier."
          },
          "status": {
            "type": "string",
            "enum": [
              "submitted",
              "working",
              "completed",
              "failed",
              "cancelled"
            ],
            "description": "Current task lifecycle state. submitted → working → completed | failed | cancelled"
          },
          "result_message": {
            "$ref": "#/components/schemas/A2AMessage",
            "description": "The agent's final response message. Only present when status is \"completed\". Contains a parts array — each part has a type (\"text\" or \"data\") and the corresponding content."
          },
          "error": {
            "type": "object",
            "description": "Error details. Only present when status is \"failed\".",
            "properties": {
              "code": {
                "type": "string"
              },
              "message": {
                "type": "string"
              }
            }
          },
          "messages": {
            "type": "array",
            "description": "Full conversation history including user and agent turns.",
            "items": {
              "$ref": "#/components/schemas/A2AMessage"
            }
          },
          "metadata": {
            "type": "object",
            "description": "Caller-supplied key/value pairs from the original task submission.",
            "additionalProperties": true
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      }
    },
    "responses": {
      "BadGateway": {
        "description": "An upstream billing provider rejected or could not complete the operation.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "ServiceUnavailable": {
        "description": "A required billing provider partnership is temporarily unavailable.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "ScopeRequired": {
        "description": "Token is valid but lacks the required scope for this endpoint. Check the endpoint description for the required scope (`discovery`, `purchase`, or `account`).\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "missing_scope": {
                "value": {
                  "error": "SCOPE_REQUIRED",
                  "message": "This endpoint requires the purchase scope."
                }
              }
            }
          }
        }
      },
      "BadRequest": {
        "description": "Invalid request — malformed body, missing required attribute, or failed validation. See `error` and `message` for details.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "validation_error": {
                "value": {
                  "error": "Bad Request",
                  "code": "MISSING_SEGMENTS",
                  "message": "segment_ids is required for filter audiences."
                }
              }
            }
          }
        }
      },
      "Unauthorized": {
        "description": "Missing or invalid Bearer token. Obtain one via POST /v1/auth/token. When a token was supplied but rejected, `code` distinguishes `TOKEN_EXPIRED` (the token's lifetime has passed — request a new one via POST /v1/auth/token and retry) from `TOKEN_INVALID` (malformed or revoked — re-authenticate).\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "missing_token": {
                "value": {
                  "error": "Unauthorized: Missing or invalid Authorization header"
                }
              },
              "token_expired": {
                "value": {
                  "error": "Unauthorized",
                  "code": "TOKEN_EXPIRED",
                  "message": "Your session has expired. Please sign in again."
                }
              }
            }
          }
        }
      },
      "Forbidden": {
        "description": "The authenticated organisation is suspended or otherwise forbidden from performing this operation.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "PaymentRequired": {
        "description": "The request cannot proceed on billing grounds. `BILLING_INSUFFICIENT_BALANCE` applies only to prepay accounts and includes `required`, `available`, and `shortfall` in USD. `BILLING_POSTPAY_CEILING_EXCEEDED` applies to an account ceiling; `BILLING_CONSUMER_POSTPAY_CEILING_EXCEEDED` applies to an agency child's routed ceiling. Both ceiling responses include projected `accrued` and configured `ceiling` in USD.\n",
        "content": {
          "application/json": {
            "schema": {
              "allOf": [
                {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                {
                  "type": "object",
                  "properties": {
                    "code": {
                      "type": "string",
                      "enum": [
                        "BILLING_INSUFFICIENT_BALANCE",
                        "BILLING_POSTPAY_CEILING_EXCEEDED",
                        "BILLING_CONSUMER_POSTPAY_CEILING_EXCEEDED"
                      ]
                    },
                    "shortfall": {
                      "type": "number"
                    },
                    "required": {
                      "type": "number"
                    },
                    "available": {
                      "type": "number"
                    },
                    "accrued": {
                      "type": "number"
                    },
                    "ceiling": {
                      "type": "number"
                    }
                  }
                }
              ]
            },
            "examples": {
              "insufficient_balance": {
                "value": {
                  "error": "Insufficient balance",
                  "code": "BILLING_INSUFFICIENT_BALANCE",
                  "message": "Insufficient effective balance",
                  "required": 1,
                  "available": 0.75,
                  "shortfall": 0.25
                }
              },
              "budget_ceiling_reached": {
                "value": {
                  "error": "Billing capacity unavailable",
                  "code": "BILLING_POSTPAY_CEILING_EXCEEDED",
                  "message": "Postpay ceiling would be exceeded",
                  "accrued": 105,
                  "ceiling": 100
                }
              }
            }
          }
        }
      },
      "QueryExecutionError": {
        "description": "The underlying analytics query failed, or (`AGGREGATION_FLOOR_NOT_MET`) a non-discovery route encountered a result below the privacy floor. `message` is always a friendly, client-safe description — the raw backend error is never returned.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "aggregation_floor_not_met": {
                "value": {
                  "error": "Bad Gateway",
                  "code": "AGGREGATION_FLOOR_NOT_MET",
                  "message": "For privacy reasons, a minimum of 50 records is required to view this information."
                }
              },
              "table_missing": {
                "value": {
                  "error": "Bad Gateway",
                  "code": "BQ_TABLE_MISSING",
                  "message": "This data is missing — it may have expired or the original upload never finished processing. Try re-uploading, or contact support if this persists."
                }
              }
            }
          }
        }
      },
      "AggregationFloorNotMet": {
        "description": "The result is below the minimum population that can be returned while preserving privacy.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": "Privacy threshold not met",
              "code": "AGGREGATION_FLOOR_NOT_MET",
              "message": "For privacy reasons, a minimum of 50 records is required to view this information."
            }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found or not accessible to the calling org.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "not_found": {
                "value": {
                  "error": "Audience not found"
                }
              }
            }
          }
        }
      },
      "Conflict": {
        "description": "Resource state conflicts with the request. For upload analysis this includes FILE_NOT_YET_UPLOADED.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "UnprocessableEntity": {
        "description": "Request is structurally valid but the resource is in a state that prevents the operation (e.g. expired, pending, or not yet uploaded). See `error` and `code` for the specific reason.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "audience_expired": {
                "value": {
                  "error": "Audience expired",
                  "code": "AUDIENCE_EXPIRED",
                  "message": "This audience version expired. Run a refresh to create a new deliverable version."
                }
              },
              "audience_pending": {
                "value": {
                  "error": "Audience not ready",
                  "code": "AUDIENCE_PENDING",
                  "message": "This audience is still being processed. Poll GET /v1/audiences/{id} until status is active."
                }
              }
            }
          }
        }
      },
      "InternalServerError": {
        "description": "Unexpected server failure.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Request throttled. Two distinct conditions return 429: **Rate limit** — too many requests per minute for your tier (`error: rate_limit_exceeded`); retry after `retry_after_seconds`. **Compute ceiling** — org-level query-scan budget exhausted (`error: compute_limit_exceeded`); resets hourly or on plan upgrade.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "examples": {
              "rate_limit": {
                "value": {
                  "error": "rate_limit_exceeded",
                  "message": "Rate limit of 60 requests/minute exceeded for tier 'starter'.",
                  "retry_after_seconds": 45
                }
              },
              "compute_limit": {
                "value": {
                  "error": "compute_limit_exceeded",
                  "message": "Org compute ceiling exceeded. Upgrade your plan or wait for the hourly reset."
                }
              }
            }
          }
        }
      },
      "OAuthError": {
        "description": "RFC 6749-shaped OAuth protocol error — `error` is a fixed machine-readable code and `error_description` is a human-readable detail. Deliberately NOT this API's usual `{error, code, message}` shape — this is the RFC's own error contract, used only by `/v1/oauth/authorize`, `/v1/oauth/token`, and `/v1/oauth/revoke`.\n",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/OAuthErrorResponse"
            }
          }
        }
      }
    }
  },
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    {
      "name": "Auth",
      "description": "Token exchange"
    },
    {
      "name": "OAuth",
      "description": "OAuth 2.0 authorization server for confidential integrations and delegated REST, MCP, and A2A clients. Authenticate on behalf of one of your customers via the standard authorization-code flow with PKCE, then use the resulting access token exactly like an API-key-derived one.\n"
    },
    {
      "name": "Triggers",
      "description": "Durable, per-event-filtered webhook-trigger subscriptions (e.g. one per Zap) — distinct from the single implicit URL configured under the Webhooks tag / `/v1/settings/webhook`. Delivery is retried with exponential backoff and dead-lettered after repeated failures. An OAuth-authenticated caller manages only the subscriptions created by its own connection; an API-key/session caller with 'account' scope manages every subscription in the org.\n"
    },
    {
      "name": "Discovery",
      "description": "Audience count and lookup (exploration)"
    },
    {
      "name": "Campaigns",
      "description": "Named workspaces that group related audiences and deliveries under a single context"
    },
    {
      "name": "Segments",
      "description": "Save and manage reusable filter definitions"
    },
    {
      "name": "Audiences",
      "description": "Build, store, and manage named audience definitions"
    },
    {
      "name": "Deliveries",
      "description": "Org-wide delivery history and status"
    },
    {
      "name": "Enrichment",
      "description": "Attribute catalog, propensity models, synchronous micro-batch matching, and async file-based identity resolution"
    },
    {
      "name": "Workflows",
      "description": "Monitor async audience pipeline progress (similarity and propensity subtypes). Poll GET /v1/workflows/{workflow_run_id} or respond to HITL gates via POST /v1/workflows/{workflow_run_id}/resume."
    },
    {
      "name": "Account",
      "description": "Balance, top-up, and subscription management"
    },
    {
      "name": "Settings",
      "description": "Self-service API keys and org configuration"
    },
    {
      "name": "Webhooks",
      "description": "**Outbound webhook notifications** — the platform POSTs to your endpoint when async operations finish.\n\n**Events fired:**\n| Event | Trigger | |---|---| | `segment.ready` | Async segment build succeeded (matched, similarity, propensity) | | `segment.failed` | Async segment build failed | | `delivery.completed` | File export/download ready | | `delivery.failed` | File export/download failed |\nOAuth partnerships may additionally subscribe their own grant to `integration.enrichment.prepared`, `integration.enrichment.failed`, `integration.file.ready`, and `integration.file.failed`. Those callbacks are delivered only to the owning grant's subscription and never to the org-wide webhook.\n\n**URL precedence** — per-request `webhook_url` > org-configured URL (Settings) > no dispatch. If an individual API request includes `webhook_url`, that URL is used for that call only; the org-level URL is not called. Omitting `webhook_url` on a request falls back to the org URL.\n\n**Payload shape** — every dispatch (org-configured URL, per-request `webhook_url`, and durable subscriptions alike) sends the same canonical envelope: `{id, type, created_at, api_version, org_id, subject, data, links}`. `type` is the event name (e.g. `segment.ready`); `subject` identifies what the event is about (`{kind, id, name}`); `data` carries `status` plus event-specific fields — see each event below for its exact shape.\n\n**Signing** — every outbound dispatch is signed. The signing secret is platform-generated — you never supply one — and shown to you exactly once: in the response of `PUT /v1/settings/webhook` (the first time a webhook is configured) or `POST /v1/settings/webhook/secret` (rotate), or in the response of `POST /v1/webhook-subscriptions` for a durable subscription. Verifying is optional; every dispatch includes: ``` X-IA-Timestamp: <unix-seconds> X-IA-Signature: v1=<hmac-hex> ``` computed as `HMAC-SHA256(secret, \"{timestamp}.{raw-request-body}\")`. To verify: reject if `timestamp` is more than 300 seconds old, then recompute the HMAC over the exact raw request body bytes and compare with `crypto.timingSafeEqual` (or equivalent) — never compare against a re-serialized/re-parsed body, since re-serialization is not guaranteed to produce identical bytes.\n"
    },
    {
      "name": "MCP",
      "description": "Model Context Protocol session management"
    },
    {
      "name": "A2A",
      "description": "Agent-to-Agent task protocol"
    },
    {
      "name": "Partnerships",
      "description": "Retained activity and reusable results produced through connected partners"
    }
  ],
  "paths": {
    "/v1/auth/token": {
      "post": {
        "summary": "Exchange API key for an access token",
        "operationId": "exchangeToken",
        "description": "Public endpoint — no token required. Exchanges an org API key (`cf_live_...`) for a short-lived Bearer access token. Include the returned `access_token` as `Authorization: Bearer <access_token>` on all subsequent protected requests. Suspended organisations and children of a suspended agency cannot exchange API keys.\n",
        "tags": [
          "Auth"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "api_key"
                ],
                "properties": {
                  "api_key": {
                    "type": "string",
                    "description": "Organisation API key in format cf_live_..."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Access token issued",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "access_token",
                    "token_type",
                    "expires_in"
                  ],
                  "properties": {
                    "access_token": {
                      "type": "string",
                      "description": "Bearer access token — include as Authorization header on protected requests"
                    },
                    "token_type": {
                      "type": "string",
                      "example": "bearer"
                    },
                    "expires_in": {
                      "type": "integer",
                      "description": "Token lifetime in seconds"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          }
        }
      }
    },
    "/v1/oauth/authorize": {
      "get": {
        "summary": "Begin an OAuth 2.0 authorization request",
        "operationId": "oauthAuthorize",
        "description": "Public — no bearer token. Redirect your user's browser here to start the standard OAuth 2.0 authorization-code flow with PKCE (RFC 6749 + RFC 7636). `code_challenge_method` must be `S256` — `plain` is rejected (RFC 9700). On success, the browser is eventually redirected back to your `redirect_uri` with a `code` (and your `state`, if you sent one) to exchange at `POST /v1/oauth/token`. On a validation failure, the browser is redirected back with `error` and `error_description` instead (RFC 6749 §4.1.2.1) — except an unknown `client_id` or an unregistered `redirect_uri`, which return 400 JSON directly rather than a redirect, since there is no verified address yet to redirect to.\n",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "parameters": [
          {
            "name": "response_type",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "code"
              ]
            }
          },
          {
            "name": "client_id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "redirect_uri",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uri"
            }
          },
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Space-delimited scopes (`discovery`, `purchase`). Defaults to `discovery`."
          },
          {
            "name": "resource",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "uri"
            },
            "description": "RFC 8707 resource URI. Required for public MCP/A2A clients. A pre-registered confidential client that omits it receives a REST-bound token.\n"
          },
          {
            "name": "state",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Opaque value echoed back on both the success and error redirects."
          },
          {
            "name": "code_challenge",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code_challenge_method",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "S256"
              ]
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirects to the consent page on success, or back to your `redirect_uri` with `error`/`error_description` (and `state`, if you sent one) on a validation failure that occurred after `redirect_uri` was verified.\n"
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/v1/oauth/token": {
      "post": {
        "summary": "Exchange an authorization code or refresh token for an access token",
        "operationId": "oauthToken",
        "description": "Confidential clients authenticate per RFC 6749 §3.2. Public clients send `client_id` with no secret and rely on PKCE or their rotating refresh token. Accepts `application/x-www-form-urlencoded` or `application/json`. Send your `client_id`/`client_secret` via HTTP Basic (`Authorization: Basic base64(client_id:client_secret)`) or as body fields. Public clients use `token_endpoint_auth_method=none`.\n`grant_type=authorization_code` requires `code`, the exact same `redirect_uri` you sent to `/authorize`, and `code_verifier` (the PKCE secret whose SHA-256 hash matches the `code_challenge` you sent to `/authorize`).\n`grant_type=refresh_token` requires `refresh_token` and rotates it — store the `refresh_token` returned in the response and discard the one you presented; it is now rejected if presented again.\n",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OAuthTokenRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "A fresh access token and a rotated refresh token, for either grant type.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthTokenResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "401": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/v1/oauth/revoke": {
      "post": {
        "summary": "Revoke a token (RFC 7009)",
        "operationId": "oauthRevoke",
        "description": "Client-authenticated, same credentials as `/v1/oauth/token`. Call this when your user disconnects the partnership. Always returns 200 with an empty body regardless of whether the presented token was valid, except when client authentication itself fails — there is nothing to distinguish for a caller who might be probing for valid tokens (RFC 7009 §2.2).\n",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "$ref": "#/components/schemas/OAuthRevokeRequest"
              }
            },
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OAuthRevokeRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Returned once the client itself authenticated, regardless of whether the presented token was valid.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/v1/oauth/register": {
      "post": {
        "summary": "Register a public OAuth client (RFC 7591 fallback)",
        "operationId": "oauthRegisterClient",
        "description": "Public and rate-limited. Registers a public PKCE client for MCP/A2A gateways that do not support Client ID Metadata Documents. No client secret is issued.\n",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/OAuthClientRegistrationRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Public client registration.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthClientRegistrationResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/OAuthError"
          },
          "401": {
            "$ref": "#/components/responses/OAuthError"
          }
        }
      }
    },
    "/.well-known/oauth-authorization-server": {
      "get": {
        "summary": "OAuth 2.0 authorization server metadata (RFC 8414)",
        "operationId": "oauthMetadata",
        "description": "Public, static metadata describing this authorization server's endpoints and capabilities.",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Authorization server metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthServerMetadata"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource": {
      "get": {
        "summary": "REST protected-resource metadata (RFC 9728)",
        "operationId": "oauthRestProtectedResourceMetadata",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "REST resource metadata.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthProtectedResourceMetadata"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource/mcp": {
      "get": {
        "summary": "MCP protected-resource metadata (RFC 9728)",
        "operationId": "oauthMcpProtectedResourceMetadata",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "MCP resource metadata used by OAuth-aware clients.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthProtectedResourceMetadata"
                }
              }
            }
          }
        }
      }
    },
    "/.well-known/oauth-protected-resource/a2a": {
      "get": {
        "summary": "A2A protected-resource metadata (RFC 9728)",
        "operationId": "oauthA2aProtectedResourceMetadata",
        "tags": [
          "OAuth"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "A2A resource metadata advertised by the Agent Card.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OAuthProtectedResourceMetadata"
                }
              }
            }
          }
        }
      }
    },
    "/v1/webhook-subscriptions": {
      "post": {
        "summary": "Create a webhook-trigger subscription",
        "operationId": "createWebhookSubscription",
        "description": "Registers a durable trigger subscription — delivery is retried with exponential backoff and dead-lettered after repeated failures (a 410 response from `target_url` dead-letters immediately and revokes the subscription — this is how Zapier signals \"this Zap was turned off\"). Requires 'account' scope, or an OAuth access token — in the OAuth case, this subscription is bound to your connection automatically. Capped at 25 active subscriptions per org.\n",
        "tags": [
          "Triggers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "target_url",
                  "events"
                ],
                "properties": {
                  "target_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Must be HTTPS and resolve to a public, non-internal address — validated at creation and re-validated before every delivery attempt."
                  },
                  "events": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                      "$ref": "#/components/schemas/NotifiableEventType"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Subscription created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookSubscriptionCreated"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "description": "The org already has 25 active subscriptions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List webhook-trigger subscriptions",
        "operationId": "listWebhookSubscriptions",
        "description": "Requires 'account' scope, or an OAuth access token — in the OAuth case, this list is scoped to subscriptions created by your own connection only.\n",
        "tags": [
          "Triggers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Subscriptions visible to the caller, most recent first.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "subscriptions"
                  ],
                  "properties": {
                    "subscriptions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/WebhookSubscription"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/webhook-subscriptions/{id}": {
      "delete": {
        "summary": "Revoke a webhook-trigger subscription",
        "operationId": "revokeWebhookSubscription",
        "description": "Requires 'account' scope, or an OAuth access token. Idempotent — revoking an already-revoked subscription still returns 204. An OAuth caller may only revoke a subscription bound to its own connection; any other subscription id returns 404 rather than 403, so existence is never leaked.\n",
        "tags": [
          "Triggers"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Subscription revoked (or was already revoked)."
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/schema/fields": {
      "get": {
        "summary": "Discover available filter attributes",
        "operationId": "getSchemaFields",
        "description": "Returns the Attribute Catalog — every attribute you can use in the `filters` array when calling the count, lookup, crosstab, and overlap endpoints. For each attribute you'll get its data type, a plain-English description, a suggested UI widget, and (for categorical attributes) the allowed values. Use this to build dynamic filter UIs or to validate attribute names before submitting a query.\nOnly filterable attributes are returned — there's no parameter to widen this set. The response is cached and refreshed nightly. Requires 'discovery' scope.\n",
        "tags": [
          "Discovery"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Attribute Catalog.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fields": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field_name": {
                            "type": "string",
                            "example": "gender"
                          },
                          "table_name": {
                            "type": "string",
                            "example": "demographics.consumers_v2"
                          },
                          "bq_type": {
                            "type": "string",
                            "example": "STRING"
                          },
                          "description": {
                            "type": "string",
                            "description": "Plain-English description of the attribute.",
                            "example": "Age bracket of the consumer (e.g. 25-34)"
                          },
                          "filterable": {
                            "type": "boolean"
                          },
                          "operators": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "example": [
                              "=",
                              "!=",
                              "IN",
                              "NOT IN"
                            ]
                          },
                          "ui_hint": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "example": "multi_select"
                          },
                          "ui_label": {
                            "type": "string",
                            "example": "Gender"
                          },
                          "enum_values": {
                            "type": [
                              "array",
                              "null"
                            ],
                            "items": {
                              "type": "string"
                            },
                            "example": [
                              "18-24",
                              "25-34",
                              "35-44",
                              "45-54",
                              "55+"
                            ]
                          },
                          "value_labels": {
                            "oneOf": [
                              {
                                "type": "object",
                                "additionalProperties": {
                                  "type": "string"
                                }
                              },
                              {
                                "type": "null"
                              }
                            ],
                            "description": "Maps raw stored codes to human-readable labels (e.g. {\"M\":\"Male\",\"F\":\"Female\"})."
                          },
                          "is_pii": {
                            "type": "boolean"
                          },
                          "groupable": {
                            "type": "boolean"
                          },
                          "anchor": {
                            "type": "boolean"
                          },
                          "data_level": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "category_display_name": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "example": "Demographics & Household"
                          },
                          "category": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "field_name",
                          "table_name",
                          "bq_type",
                          "description",
                          "filterable",
                          "operators",
                          "ui_hint",
                          "ui_label",
                          "enum_values",
                          "value_labels",
                          "is_pii",
                          "groupable",
                          "anchor",
                          "data_level",
                          "category_display_name"
                        ]
                      }
                    },
                    "cached_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  },
                  "required": [
                    "fields",
                    "cached_at"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/discovery/count": {
      "post": {
        "summary": "Count records matching criteria",
        "operationId": "discoveryCount",
        "description": "Returns the number of records matching the given criteria. Counts the bytes scanned toward your organization's compute limit. Requires 'discovery' scope.\nProvide exactly one of: `filters` (flat array), `filter_groups` (compound AND/OR groups), `segment_id` (a saved segment), `audience_id` (a saved audience), or `segment_ids` + `set_logic` (combine multiple segments). `filters` and `filter_groups` count as the same input — if both are sent, `filter_groups` wins. Sending more than one input returns 400 `AMBIGUOUS_INPUT`; sending none returns 400 `NO_INPUT_SPECIFIED`.\nCount, lookup, crosstab, and overlap all preserve full group/combinator structure: each included segment's own AND/OR groups are evaluated intact, and an audience's excluded segments are always subtracted (AND NOT), regardless of `set_logic`.\n",
        "tags": [
          "Discovery"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "array",
                    "description": "Flat filter array (recommended).",
                    "items": {
                      "$ref": "#/components/schemas/Filter"
                    }
                  },
                  "filter_groups": {
                    "type": "array",
                    "description": "Compound boolean filter groups (alternative to `filters`). Each group has a `combinator` (AND/OR) and a `filters` array.\n",
                    "items": {
                      "$ref": "#/components/schemas/FilterGroup"
                    }
                  },
                  "segment_id": {
                    "type": "string",
                    "description": "ID of a saved segment to count."
                  },
                  "audience_id": {
                    "type": "string",
                    "description": "ID of a saved audience to count."
                  },
                  "segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "IDs of multiple segments to combine (requires `set_logic`)."
                  },
                  "set_logic": {
                    "type": "string",
                    "enum": [
                      "union",
                      "intersection"
                    ],
                    "description": "How to combine segment_ids: union = match ANY segment (OR); intersection = match EVERY segment (AND).\n"
                  },
                  "excluded_segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ad-hoc \"A minus B\" preview — segments to subtract from the `segment_ids` composition, without persisting an audience. Only meaningful alongside `segment_ids`; do not send `set_logic: 'exclusion'` (invalid) — exclusion is always modeled via this separate param.\n"
                  },
                  "group_by": {
                    "type": "string",
                    "description": "Optional attribute to group counts by (e.g. 'state', 'gender')."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Record count result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "bytes_scanned": {
                      "type": "integer"
                    },
                    "estimated_cost_usd": {
                      "type": "number"
                    },
                    "matched_count": {
                      "type": "integer",
                      "description": "Only present when the resolved composition includes at least one included matched-subtype segment — the subset of `count` that came from an identity-resolution match.\n"
                    },
                    "groups": {
                      "type": "array",
                      "description": "Present when `group_by` is specified.\n",
                      "items": {
                        "type": "object",
                        "properties": {
                          "group_value": {
                            "type": "string"
                          },
                          "count": {
                            "type": "integer"
                          }
                        },
                        "required": [
                          "group_value",
                          "count"
                        ]
                      },
                      "example": [
                        {
                          "group_value": "California",
                          "count": 1200000
                        },
                        {
                          "group_value": "Texas",
                          "count": 980000
                        }
                      ]
                    },
                    "breakdowns": {
                      "type": "object",
                      "description": "Present by default (unless `group_by` is set) when the resolved composition supports them — a single filter-based segment/audience or raw filters. Not yet available for non-filter or mixed-subtype compositions.\n",
                      "properties": {
                        "age": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "bucket": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "gender": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "string"
                              },
                              "bucket": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "geography": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "string"
                              },
                              "bucket": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "behavioral": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "string"
                              },
                              "bucket": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        },
                        "financial": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "value": {
                                "type": "string"
                              },
                              "bucket": {
                                "type": "string"
                              },
                              "count": {
                                "type": "integer"
                              }
                            }
                          }
                        }
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Present only when the resolved segment_id/segment_ids/ audience_id composition includes a non-filter or mixed subtype AND `group_by`/breakdowns were requested — those are not yet available for non-filter or mixed-subtype compositions, so the count is still returned but without the requested grouping.\n"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "422": {
            "$ref": "#/components/responses/AggregationFloorNotMet"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/QueryExecutionError"
          }
        }
      }
    },
    "/v1/discovery/lookup": {
      "post": {
        "summary": "Return masked sample rows matching criteria",
        "operationId": "discoveryLookup",
        "description": "Returns up to 25 masked sample rows matching the criteria. PII attributes are masked or omitted. Requires 'discovery' scope.\nProvide exactly one of: `filters`, `filter_groups`, `segment_id`, `audience_id`, or `segment_ids` + `set_logic` (same input shapes as /v1/discovery/count). Sending more than one returns 400 `AMBIGUOUS_INPUT`; sending none returns 400 `NO_INPUT_SPECIFIED`.\nIf you pass a segment, audience, or filter groups, full group/combinator structure is preserved — each segment's own AND/OR groups are evaluated intact, multiple composed segments are combined by `set_logic`, and an audience's excluded segments are always subtracted (AND NOT), regardless of `set_logic`.\n",
        "tags": [
          "Discovery"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "filters": {
                    "type": "array",
                    "description": "Flat filter array (recommended).",
                    "items": {
                      "$ref": "#/components/schemas/Filter"
                    }
                  },
                  "filter_groups": {
                    "type": "array",
                    "description": "Compound boolean filter groups.",
                    "items": {
                      "$ref": "#/components/schemas/FilterGroup"
                    }
                  },
                  "segment_id": {
                    "type": "string",
                    "description": "ID of a saved segment to sample from."
                  },
                  "audience_id": {
                    "type": "string",
                    "description": "ID of a saved audience to sample from."
                  },
                  "segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "IDs of multiple segments to combine (requires `set_logic`)."
                  },
                  "set_logic": {
                    "type": "string",
                    "enum": [
                      "union",
                      "intersection"
                    ],
                    "description": "How to combine segment_ids: union = match ANY segment (OR); intersection = match EVERY segment (AND).\n"
                  },
                  "excluded_segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ad-hoc \"A minus B\" preview — segments to subtract from the `segment_ids` composition, without persisting an audience. Only meaningful alongside `segment_ids`; do not send `set_logic: 'exclusion'` (invalid).\n"
                  },
                  "limit": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 25,
                    "default": 10
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sample rows.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "rows": {
                      "type": "array",
                      "description": "Up to `limit` anonymised sample rows. PII attributes (email, phone, address) are masked or omitted. `iag_person_id` is always null.\n",
                      "items": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "string"
                        }
                      }
                    },
                    "total_matched": {
                      "type": "integer",
                      "description": "Total records matching the criteria (may exceed rows.length, which is capped at `limit`)."
                    },
                    "bytes_scanned": {
                      "type": "integer"
                    },
                    "estimated_cost_usd": {
                      "type": "number"
                    },
                    "available_fields": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Field names present on the returned rows (after PII masking/omission)."
                    }
                  },
                  "required": [
                    "rows",
                    "total_matched",
                    "bytes_scanned",
                    "estimated_cost_usd",
                    "available_fields"
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "422": {
            "$ref": "#/components/responses/AggregationFloorNotMet"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/QueryExecutionError"
          }
        }
      }
    },
    "/v1/discovery/crosstab": {
      "post": {
        "summary": "Cross-tabulate two dimensions across a population",
        "operationId": "discoveryCrosstab",
        "description": "Returns a pivot table of counts for two categorical dimensions (e.g. state × gender). Requires 'discovery' scope.\nProvide exactly one of: `filters`, `filter_groups`, `segment_id`, `audience_id`, or `segment_ids` + `set_logic` to define the population (same input shapes as /v1/discovery/count). Sending more than one returns 400 `AMBIGUOUS_INPUT`; sending none returns 400 `NO_INPUT_SPECIFIED`. A 400 `SAME_FIELD` is returned if `row_field` equals `col_field`.\nIf you pass a segment, audience, or filter groups, full group/combinator structure is preserved — each segment's own AND/OR groups are evaluated intact, multiple composed segments are combined by `set_logic`, and an audience's excluded segments are always subtracted (AND NOT), regardless of `set_logic`.\n",
        "tags": [
          "Discovery"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "row_field",
                  "col_field"
                ],
                "properties": {
                  "filters": {
                    "type": "array",
                    "description": "Flat filter array defining the population.",
                    "items": {
                      "$ref": "#/components/schemas/Filter"
                    }
                  },
                  "filter_groups": {
                    "type": "array",
                    "description": "Compound boolean filter groups (alternative to `filters`).",
                    "items": {
                      "$ref": "#/components/schemas/FilterGroup"
                    }
                  },
                  "segment_id": {
                    "type": "string",
                    "description": "ID of a saved segment defining the population."
                  },
                  "audience_id": {
                    "type": "string",
                    "description": "ID of a saved audience defining the population."
                  },
                  "segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "IDs of multiple segments to combine (requires `set_logic`)."
                  },
                  "set_logic": {
                    "type": "string",
                    "enum": [
                      "union",
                      "intersection"
                    ],
                    "description": "How to combine segment_ids: union = match ANY segment (OR); intersection = match EVERY segment (AND).\n"
                  },
                  "excluded_segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Ad-hoc \"A minus B\" preview — segments to subtract from the `segment_ids` composition, without persisting an audience. Only meaningful alongside `segment_ids`.\n"
                  },
                  "row_field": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64,
                    "description": "Attribute for the row dimension (e.g. \"state\")."
                  },
                  "col_field": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 64,
                    "description": "Attribute for the column dimension (e.g. \"gender\"). Must differ from row_field."
                  },
                  "row_label": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "Human-readable row label (defaults to row_field)."
                  },
                  "col_label": {
                    "type": "string",
                    "maxLength": 80,
                    "description": "Human-readable column label (defaults to col_field)."
                  },
                  "max_rows": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 20,
                    "description": "Max distinct row values, ordered by total count desc."
                  },
                  "max_cols": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 20,
                    "default": 10,
                    "description": "Max distinct column values, ordered by total count desc."
                  }
                }
              },
              "example": {
                "segment_id": "seg_1a2b3c",
                "row_field": "state",
                "col_field": "gender",
                "row_label": "State",
                "col_label": "Gender",
                "max_rows": 10
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Pivot table result.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "row_field",
                    "col_field",
                    "row_label",
                    "col_label",
                    "rows",
                    "columns",
                    "cells",
                    "total_count"
                  ],
                  "properties": {
                    "row_field": {
                      "type": "string"
                    },
                    "col_field": {
                      "type": "string"
                    },
                    "row_label": {
                      "type": "string",
                      "description": "Echoes row_label (defaults to row_field)."
                    },
                    "col_label": {
                      "type": "string",
                      "description": "Echoes col_label (defaults to col_field)."
                    },
                    "rows": {
                      "type": "array",
                      "description": "Distinct row values, ordered by total count desc.",
                      "items": {
                        "type": "string"
                      }
                    },
                    "columns": {
                      "type": "array",
                      "description": "Distinct column values, ordered by total count desc.",
                      "items": {
                        "type": "string"
                      }
                    },
                    "cells": {
                      "type": "object",
                      "description": "cells[rowValue][colValue] = record count.",
                      "additionalProperties": {
                        "type": "object",
                        "additionalProperties": {
                          "type": "integer"
                        }
                      }
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "Total records matching the population input."
                    }
                  }
                },
                "example": {
                  "row_field": "state",
                  "col_field": "gender",
                  "row_label": "State",
                  "col_label": "Gender",
                  "rows": [
                    "CA",
                    "TX"
                  ],
                  "columns": [
                    "F",
                    "M"
                  ],
                  "cells": {
                    "CA": {
                      "F": 610230,
                      "M": 588112
                    },
                    "TX": {
                      "F": 402881,
                      "M": 399124
                    }
                  },
                  "total_count": 2000347
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "422": {
            "$ref": "#/components/responses/AggregationFloorNotMet"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "$ref": "#/components/responses/QueryExecutionError"
          }
        }
      }
    },
    "/v1/discovery/overlap": {
      "post": {
        "summary": "Calculate record overlap between two criteria sets",
        "operationId": "discoveryOverlap",
        "description": "Computes the number and percentage of records that appear in both of two criteria sets, plus side-by-side demographic deltas. Requires 'discovery' scope.\nEach side (`a` and `b`) independently accepts exactly one discovery input shape (`filters`, `filter_groups`, `segment_id`, `audience_id`, or `segment_ids` + `set_logic`) — the legacy flat `audience_id_a` / `audience_id_b` fields are also still accepted. A side that provides more than one shape, or combines the structured object with its legacy field, returns 400 `AMBIGUOUS_INPUT`. A side that provides nothing returns 400 `NO_INPUT_SPECIFIED`.\nEach side preserves full group/combinator structure independently: if a side is a segment, audience, or filter groups, each segment's own AND/OR groups are evaluated intact, multiple composed segments are combined by `set_logic`, and an audience's excluded segments are always subtracted (AND NOT), regardless of `set_logic` — before the two sides are joined for the overlap count.\n",
        "tags": [
          "Discovery"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "a": {
                    "$ref": "#/components/schemas/DiscoveryInput",
                    "description": "Side A. Mutually exclusive with `audience_id_a`."
                  },
                  "b": {
                    "$ref": "#/components/schemas/DiscoveryInput",
                    "description": "Side B. Mutually exclusive with `audience_id_b`."
                  },
                  "audience_id_a": {
                    "type": "string",
                    "description": "(Legacy) First audience ID. Mutually exclusive with `a`."
                  },
                  "audience_id_b": {
                    "type": "string",
                    "description": "(Legacy) Second audience ID. Mutually exclusive with `b`."
                  }
                }
              },
              "example": {
                "a": {
                  "segment_id": "seg_1a2b3c"
                },
                "b": {
                  "audience_id": "aud_9z8y7x"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Overlap result with demographic deltas.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "a_name",
                    "b_name",
                    "a_count",
                    "b_count",
                    "overlap_count",
                    "overlap_pct",
                    "demographic_deltas"
                  ],
                  "properties": {
                    "a_name": {
                      "type": "string"
                    },
                    "b_name": {
                      "type": "string"
                    },
                    "a_count": {
                      "type": "integer"
                    },
                    "b_count": {
                      "type": "integer"
                    },
                    "overlap_count": {
                      "type": "integer"
                    },
                    "overlap_pct": {
                      "type": "number",
                      "description": "Overlap as a percentage of the smaller side."
                    },
                    "demographic_deltas": {
                      "type": "array",
                      "description": "Side-by-side demographic comparison. Each entry: {metric, label, a, b, delta}.\n",
                      "items": {
                        "type": "object",
                        "required": [
                          "metric",
                          "label",
                          "a",
                          "b",
                          "delta"
                        ],
                        "properties": {
                          "metric": {
                            "type": "string"
                          },
                          "label": {
                            "type": "string"
                          },
                          "a": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "b": {
                            "type": [
                              "number",
                              "null"
                            ]
                          },
                          "delta": {
                            "type": [
                              "number",
                              "null"
                            ]
                          }
                        }
                      }
                    },
                    "distribution_a": {
                      "type": "array",
                      "description": "Optional paired distribution buckets for side A.",
                      "items": {
                        "$ref": "#/components/schemas/DistributionBucket"
                      }
                    },
                    "distribution_b": {
                      "type": "array",
                      "description": "Optional paired distribution buckets for side B.",
                      "items": {
                        "$ref": "#/components/schemas/DistributionBucket"
                      }
                    },
                    "distribution_field": {
                      "type": "string",
                      "description": "Attribute the paired distributions are bucketed on."
                    },
                    "distribution_label": {
                      "type": "string",
                      "description": "Human-readable label for the distribution attribute."
                    },
                    "age_distribution_a": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DistributionBucket"
                      }
                    },
                    "age_distribution_b": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DistributionBucket"
                      }
                    },
                    "gender_distribution_a": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DistributionBucket"
                      }
                    },
                    "gender_distribution_b": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DistributionBucket"
                      }
                    },
                    "warnings": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "Present only when at least one side resolves to a non-filter or mixed-subtype composition. Two cases: \"One or both sides had no composable segments\" when a side's composition is empty (counts are all 0); or \"Demographic breakdowns are not yet available for non-filter or mixed-subtype compositions\" when counts/ overlap were computed directly but demographic_deltas could not be (returned as an empty array in that case).\n"
                    }
                  }
                },
                "example": {
                  "a_name": "CA Homeowners",
                  "b_name": "TX Families",
                  "a_count": 1204551,
                  "b_count": 988102,
                  "overlap_count": 51233,
                  "overlap_pct": 5.2,
                  "demographic_deltas": [
                    {
                      "metric": "median_age",
                      "label": "Median age",
                      "a": 44,
                      "b": 39,
                      "delta": -5
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "422": {
            "$ref": "#/components/responses/AggregationFloorNotMet"
          },
          "502": {
            "$ref": "#/components/responses/QueryExecutionError"
          }
        }
      }
    },
    "/v1/quote": {
      "post": {
        "summary": "Delivery quote",
        "operationId": "quoteDelivery",
        "description": "Returns a price estimate for a proposed delivery without executing it. Provide the audience ID, destination, and optionally `field_list` or `template_id` (mutually exclusive) to receive a structured cost breakdown and credit balance information. If neither is provided, the Standard IAG attribute set is used. No commitment is made and nothing is stored. Download is the currently active destination; LiveRamp and Narrative return 503 before a quote or billable record is created until their partner pushes are implemented.\nReturns two cost figures: `cost_estimate` is license-aware (identities this audience already holds an active 12-month license for elsewhere are excluded from the billable count) and `max_cost_estimate` is the worst-case ceiling assuming no identity is pre-licensed. See `license_pricing.status` for whether `cost_estimate` reflects a live or cached computation, or degraded non-blockingly to the max figure.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Deliveries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "audience_id",
                  "destination"
                ],
                "properties": {
                  "audience_id": {
                    "type": "string",
                    "description": "ID of the audience to quote a delivery for.\n"
                  },
                  "destination": {
                    "type": "string",
                    "enum": [
                      "download",
                      "liveramp",
                      "narrative"
                    ],
                    "description": "Intended destination. `liveramp` and `narrative` currently return `DELIVERY_DESTINATION_UNAVAILABLE` without billing.\n"
                  },
                  "template_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Enrichment template ID. Only `standard_iag` is valid. Mutually exclusive with `field_list`. If neither is provided, the Standard IAG attribute set (all attributes with `product_usage` containing `audience`) is used.\n"
                  },
                  "field_list": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    },
                    "description": "Explicit attribute list — every attribute must be a valid audience attribute (see GET /v1/catalog/fields). Mutually exclusive with `template_id`. If neither is provided, the Standard IAG attribute set is used.\n"
                  },
                  "include_unmatched": {
                    "type": "boolean",
                    "default": true,
                    "description": "Include unmatched input rows in matched-audience output."
                  },
                  "output_format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "avro",
                      "json",
                      "jsonl"
                    ],
                    "default": "csv",
                    "description": "Artifact format being quoted, independent of matched-input format.\n"
                  },
                  "output_compression": {
                    "type": "string",
                    "enum": [
                      "none",
                      "gzip"
                    ],
                    "default": "none",
                    "description": "gzip is supported for csv/json/jsonl. Avro uses native DEFLATE and rejects outer gzip.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Price quote — no commitment made.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "audience_id",
                    "subtype",
                    "destination",
                    "template_id",
                    "field_list",
                    "output_format",
                    "output_compression",
                    "already_delivered",
                    "quote_id",
                    "cost_estimate",
                    "max_cost_estimate",
                    "license_pricing",
                    "record_count",
                    "match_count",
                    "billing_model",
                    "available_balance",
                    "sufficient_balance",
                    "shortfall",
                    "budget_ceiling",
                    "accrued_balance"
                  ],
                  "properties": {
                    "audience_id": {
                      "type": "string"
                    },
                    "subtype": {
                      "type": "string",
                      "enum": [
                        "filter",
                        "matched",
                        "similarity",
                        "propensity"
                      ]
                    },
                    "destination": {
                      "type": "string"
                    },
                    "template_id": {
                      "type": [
                        "string",
                        "null"
                      ]
                    },
                    "field_list": {
                      "type": [
                        "array",
                        "null"
                      ],
                      "items": {
                        "type": "string"
                      }
                    },
                    "output_format": {
                      "type": "string",
                      "enum": [
                        "csv",
                        "avro",
                        "json",
                        "jsonl"
                      ]
                    },
                    "output_compression": {
                      "type": "string",
                      "enum": [
                        "none",
                        "gzip"
                      ]
                    },
                    "already_delivered": {
                      "type": "boolean",
                      "description": "`true` if this billing identity (audience composition and segment revisions, resolved fields, unmatched-row policy, and destination) has already been delivered. Artifact format/compression are deliberately excluded, so changing only those axes permits a free re-export.\n"
                    },
                    "quote_id": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "ID of the persisted quote backing this estimate. `null` when `already_delivered` is `true` (a free re-delivery has no pending cost, so nothing is persisted).\n"
                    },
                    "cost_estimate": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/CostEstimate"
                        }
                      ],
                      "description": "The license-aware, expected-to-be-charged estimate — reflects any active licenses this audience's identities already hold elsewhere (see license_pricing.status). Equals max_cost_estimate exactly when license_pricing.status is unavailable.\n"
                    },
                    "max_cost_estimate": {
                      "allOf": [
                        {
                          "$ref": "#/components/schemas/CostEstimate"
                        }
                      ],
                      "description": "The worst-case ceiling, computed as if no identity in this audience were already licensed anywhere. Always present and always the same value regardless of license_pricing.status — use this for a stable upper bound.\n"
                    },
                    "license_pricing": {
                      "$ref": "#/components/schemas/LicensePricing"
                    },
                    "record_count": {
                      "type": "integer",
                      "description": "Current audience size.\n"
                    },
                    "match_count": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "matched subtype only — resolved rows from most recent delivery.\n"
                    },
                    "billing_model": {
                      "type": "string",
                      "enum": [
                        "prepay",
                        "postpay"
                      ],
                      "description": "Resolved from the org's own billing_model, or 'postpay' for parent_billed (agency) orgs. Determines which of available_balance/shortfall vs. budget_ceiling/accrued_balance is populated below.\n"
                    },
                    "available_balance": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Prepay only — effective credit balance after holds. Null for postpay/agency orgs, which have no credit balance.\n"
                    },
                    "sufficient_balance": {
                      "type": "boolean",
                      "description": "Prepay: whether available_balance ≥ total_cost. Postpay/agency: whether this delivery would stay within the org's/agency's optional budget ceiling (always true when no ceiling is set).\n"
                    },
                    "shortfall": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Prepay only — credits needed beyond current balance (0 when sufficient). Null for postpay/agency.\n"
                    },
                    "budget_ceiling": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Postpay/agency only — the org's configured budget ceiling in USD. Null if unset (unlimited) or for prepay.\n"
                    },
                    "accrued_balance": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Postpay/agency only — accrued USD spend so far this cycle. Null for prepay.\n"
                    }
                  }
                },
                "example": {
                  "audience_id": "aud_abc123",
                  "subtype": "filter",
                  "destination": "download",
                  "template_id": "standard_iag",
                  "field_list": null,
                  "output_format": "csv",
                  "output_compression": "none",
                  "already_delivered": false,
                  "quote_id": "q_abc123",
                  "cost_estimate": {
                    "base_cost": 0.08,
                    "match_cost": 0,
                    "field_cost": 0.04,
                    "destination_cost": 0,
                    "total_cost": 0.12,
                    "unit_price": 0.0015,
                    "billing_count": 80000
                  },
                  "max_cost_estimate": {
                    "base_cost": 0.08,
                    "match_cost": 0,
                    "field_cost": 0.04,
                    "destination_cost": 0,
                    "total_cost": 0.12,
                    "unit_price": 0.0015,
                    "billing_count": 80000
                  },
                  "license_pricing": {
                    "status": "live",
                    "evaluated_at": "2026-08-21T12:00:00.000Z",
                    "already_licensed_count": 0
                  },
                  "record_count": 80000,
                  "match_count": null,
                  "billing_model": "prepay",
                  "available_balance": 5,
                  "sufficient_balance": true,
                  "shortfall": 0,
                  "budget_ceiling": null,
                  "accrued_balance": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/campaigns": {
      "post": {
        "summary": "Create a campaign",
        "operationId": "createCampaign",
        "description": "Creates a new campaign workspace. A **Campaign** is a named container that groups related audiences and deliveries under a single context — useful for organising a body of work such as a quarterly prospecting initiative or a product-launch outreach.\n\n**Campaign association is optional.** Audiences, file-match jobs, and deliveries all work independently without a campaign. Use campaigns when you want to track related work together — e.g. filtering `GET /v1/operations` to `campaign_id` for a specific initiative's activity.\n\nAfter creation, associate audiences with the campaign by:\n- Passing `campaign_id` to `POST /v1/audiences` or `POST /v1/match/file` at creation time (auto-links on creation).\n- Calling `POST /v1/campaigns/{id}/audiences/{audience_id}` to link an existing audience.\n\nRequires 'purchase' scope.\n",
        "tags": [
          "Campaigns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Display name for the campaign.\n"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Optional campaign brief, intended market, goals, and constraints."
                  }
                }
              },
              "examples": {
                "basic": {
                  "summary": "Create a campaign",
                  "value": {
                    "name": "Q3 2026 Prospecting"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Campaign created.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "get": {
        "summary": "List campaigns",
        "operationId": "listCampaigns",
        "description": "Returns all campaigns for the org, ordered by most recently updated. Filter by status to scope to active or archived campaigns. Requires 'discovery' scope.\n",
        "tags": [
          "Campaigns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "archived"
              ]
            },
            "description": "Filter by campaign status. Omit to return all campaigns.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign list.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "campaigns": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Campaign"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/campaigns/{id}": {
      "get": {
        "summary": "Get a campaign",
        "operationId": "getCampaign",
        "description": "Returns full campaign details including an expanded `audiences` array with resolved segment names and record counts for each linked audience.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Campaigns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Campaign ID returned by POST /v1/campaigns.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Campaign with expanded audience list.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "summary": "Update a campaign",
        "operationId": "updateCampaign",
        "description": "Rename a campaign, revise its brief, or change its status. Supported status values: `archived` (soft-delete) and `active` (restore from archived).\n      Requires 'purchase' scope.\n",
        "tags": [
          "Campaigns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "New display name.\n"
                  },
                  "description": {
                    "type": "string",
                    "maxLength": 1000,
                    "description": "Campaign brief; send an empty string to clear it."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "archived",
                      "active"
                    ],
                    "description": "`archived` — soft-deletes the campaign (excluded from list by default). `active` — restores a previously archived campaign.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated campaign.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Campaign"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Archive a campaign",
        "operationId": "archiveCampaign",
        "description": "Soft-archives the campaign by setting its status to `archived`. The campaign is not deleted and can be retrieved by listing with `?status=archived`. Idempotent — archiving an already-archived campaign returns 204.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Campaigns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Campaign archived.\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/campaigns/{id}/audiences/{audience_id}": {
      "post": {
        "summary": "Link an audience to a campaign",
        "operationId": "linkAudienceToCampaign",
        "description": "Associates an existing audience with a campaign. An audience can belong to at most one campaign at a time (exclusive ownership). Returns `409` if the audience is already linked to a **different** campaign — unlink it first. Idempotent: linking an already-linked audience to the **same** campaign returns `204` without error.\n\nFor new audiences, it is simpler to pass `campaign_id` directly to `POST /v1/audiences` or `POST /v1/match/file` to auto-link at creation time.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Campaigns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Campaign ID.\n"
          },
          {
            "name": "audience_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience ID to link.\n"
          }
        ],
        "responses": {
          "204": {
            "description": "Audience linked (or was already linked to this campaign).\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Audience is already linked to a different campaign.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "linked_campaign_id": {
                      "type": "string",
                      "description": "The campaign this audience is currently linked to.\n"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "delete": {
        "summary": "Unlink an audience from a campaign",
        "operationId": "unlinkAudienceFromCampaign",
        "description": "Removes the audience from the campaign's audience list. The audience itself is **not** deleted and can be linked to a different campaign or used independently afterward. Idempotent.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Campaigns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Campaign ID.\n"
          },
          {
            "name": "audience_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience ID to unlink.\n"
          }
        ],
        "responses": {
          "204": {
            "description": "Audience unlinked.\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/operations": {
      "get": {
        "summary": "Get the org's operational event feed (Ops Rail)",
        "operationId": "getOperations",
        "description": "Returns operational events (segment builds, refreshes, deliveries, and micro-batch matches) as a persisted, append-only log. A single async operation (e.g. a matched-segment build) can appear as multiple events over time (`started`, `completed` or `failed`), each with its own timestamp and status.\n\n**Scope:** Defaults to every event across the org (`scope=org`). Pass `scope=user` to see only events initiated by the caller. Optionally narrow further with `event_type` and `campaign_id`. \"org\" always means whichever org the caller is currently acting as — for a Parent-Org/ agency member this is the child org they've toggled to (`x-target-org-id`), not a fixed home org. Returns 400 `NO_ORG_CONTEXT` if the caller has no org context at all (an agency member who hasn't toggled to a child org yet).\n\n**Window:** Defaults to the last 3 hours (`since` omitted). Pass an explicit `since`/`before` window to look further back — this log is retained for 2 years (it also backs the org's Admin Audit Log view).\n\nRequires 'discovery' scope.\n",
        "tags": [
          "Campaigns"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "scope",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "enum": [
                "org",
                "user"
              ],
              "default": "org"
            },
            "description": "`org` returns events from every member of the caller's org; `user` restricts results to events initiated by the caller.\n"
          },
          {
            "name": "since",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only return events at or after this timestamp. Defaults to 3 hours before the request time.\n"
          },
          {
            "name": "before",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Only return events strictly before this timestamp — combine with the oldest `ts` from a prior page to page further back in time.\n"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "default": 200,
              "maximum": 500
            }
          },
          {
            "name": "event_type",
            "in": "query",
            "required": false,
            "schema": {
              "$ref": "#/components/schemas/OperationEventType"
            }
          },
          {
            "name": "campaign_id",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Restrict to events associated with this campaign (most segment, audience, and micro-batch-match activity has no campaign association at all — this filter only matches events that do).\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Operational events, most recent first.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "events",
                    "total"
                  ],
                  "properties": {
                    "events": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/OperationEvent"
                      }
                    },
                    "total": {
                      "type": "integer"
                    }
                  }
                },
                "examples": {
                  "mixed_events": {
                    "summary": "A completed delivery and an in-progress segment build",
                    "value": {
                      "events": [
                        {
                          "event_id": "evt_abc",
                          "org_id": "org_789",
                          "type": "delivery.completed",
                          "status": "completed",
                          "is_terminal": true,
                          "operation_key": "delivery:del_abc",
                          "actor_uid": "user_123",
                          "campaign_id": null,
                          "subject": {
                            "kind": "delivery",
                            "id": "del_abc",
                            "name": null
                          },
                          "subtype": null,
                          "category": "operational",
                          "payload": {
                            "destination": "download",
                            "record_count": 47821
                          },
                          "ts": "2026-05-30T18:08:43Z"
                        },
                        {
                          "event_id": "evt_def",
                          "org_id": "org_789",
                          "type": "segment.build.started",
                          "status": "pending",
                          "is_terminal": false,
                          "operation_key": "segment:seg_456",
                          "actor_uid": "user_123",
                          "campaign_id": null,
                          "subject": {
                            "kind": "segment",
                            "id": "seg_456",
                            "name": "Lookalike v2"
                          },
                          "subtype": "similarity",
                          "category": "operational",
                          "payload": {},
                          "ts": "2026-05-30T18:10:00Z"
                        }
                      ],
                      "total": 2
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/segments": {
      "post": {
        "summary": "Create a segment",
        "operationId": "createSegment",
        "description": "Creates a typed cohort definition (segment). The `subtype` attribute controls which additional parameters are required and what the platform does next.\n\n**Subtypes:**\n\n- **filter** (default) — Saves a named filter expression. Returns `status: active`\n  immediately. Use `filters`/`filter_groups` for the criteria.\n  Pass `create_audience: true` to also create a thin audience wrapper\n  referencing this segment. Default: `false` — the segment is saved for\n  later composition into an audience once iterative refinement is complete.\n\n\n- **matched** — Upload your own customer list for identity resolution.\n  Returns `status: pending` + `upload_url` (30-minute signed upload URL) —\n  or `upload_urls` (an array, one per shard) when `shard_count` > 1,\n  in which case `upload_url` is absent. Input may be raw or gzip.\n  With `hitl` unset/false (default): upload your file(s) and the platform\n  runs column mapping and identity resolution automatically once every\n  declared shard has been uploaded, transitioning\n  to `status: active` when complete — no further calls required. With\n  `hitl: true`: the upload is staged instead of resolving automatically —\n  see the `hitl` field description below for the full preview/confirm/cancel\n  sequence. When `create_audience: true` (default for matched), a thin\n  audience wrapper is also created automatically.\n\n\n- **similarity** — Lookalike modelling. Provide `seed_description` (natural language\n  ICP persona) or `seed_segment_id` (an existing segment) — not both — to launch a\n  new workflow. Returns `status: pending` + `workflow_run_id`.\n  Alternatively, provide `generation_metadata_id` (an existing `icp_id`) with no seed\n  fields to attach that already-completed ICP run directly — no workflow is launched,\n  `status: active` immediately. Default `create_audience: false`.\n\n\n- **propensity** — ML propensity scoring. Provide `positive_class_segment_id`\n  (an existing segment whose members are the positive training class) to launch a\n  new workflow. Returns `status: pending` + `workflow_run_id`.\n  Alternatively, provide `generation_metadata_id` (an existing `model_id`) with no\n  `positive_class_segment_id` to attach that already-completed model run directly —\n  no workflow is launched, `status: active` immediately. Default `create_audience: false`.\n\n\nPoll `GET /v1/segments/{id}` for status, or configure a webhook to receive `segment.ready` / `segment.failed` events.\n      Requires 'purchase' scope.\n\nFor `filter` subtype: `filters`/`filter_groups` are validated against the live Field Catalog before the segment is created — an unknown field, a disallowed operator for that field, or an invalid enum value returns 400 `INVALID_FILTER`. If the Field Catalog itself is temporarily unreachable, returns 502 `FILTER_CATALOG_UNAVAILABLE` rather than creating an unvalidated segment.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Display name for the segment.\n"
                  },
                  "subtype": {
                    "type": "string",
                    "enum": [
                      "filter",
                      "matched",
                      "similarity",
                      "propensity"
                    ],
                    "default": "filter",
                    "description": "Segment subtype. Determines which additional attributes are required.\n"
                  },
                  "create_audience": {
                    "type": "boolean",
                    "description": "When true, automatically creates a thin audience wrapper referencing this segment. Defaults: filter=true, matched=true, similarity=false, propensity=false.\n"
                  },
                  "filters": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Filter"
                    },
                    "description": "filter subtype only. Flat filter array (legacy). Prefer filter_groups."
                  },
                  "filter_groups": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/FilterGroup"
                    },
                    "description": "filter subtype only. Grouped filter conditions."
                  },
                  "file_format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "avro",
                      "json",
                      "jsonl"
                    ],
                    "default": "csv",
                    "description": "matched subtype only. Input file format. csv (default) treats every column as a single scalar value — multi-value/array attributes are not supported in csv. avro, json, and jsonl support native array columns for attributes that require them — json and jsonl are both parsed as newline-delimited JSON (one record object per line), not a single top-level JSON array.\n"
                  },
                  "compression": {
                    "type": "string",
                    "enum": [
                      "none",
                      "gzip"
                    ],
                    "default": "none",
                    "description": "matched subtype only. gzip means the complete underlying format file is a gzip stream. Upload raw gzip bytes using the underlying format's Content-Type and no Content-Encoding. gzip is not supported with file_format=avro (avro is already internally compressed) — rejected with 400 UNSUPPORTED_INPUT_COMPRESSION.\n"
                  },
                  "shard_count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 1,
                    "description": "matched subtype only. Set > 1 for same-format shards. Every CSV shard must include the same header row, and all shards must share schema and compression. Returns upload_urls instead of upload_url.\n"
                  },
                  "session_id": {
                    "type": "string",
                    "description": "Optional chat session identifier used to reconnect progress for an asynchronously generated similarity or propensity segment.\n"
                  },
                  "seed_description": {
                    "type": "string",
                    "description": "similarity subtype only. Natural language description of the target ICP persona. Provide either seed_description or seed_segment_id, not both.\n"
                  },
                  "seed_segment_id": {
                    "type": "string",
                    "description": "similarity subtype only. ID of an existing segment to use as the lookalike seed. Provide either seed_description or seed_segment_id, not both.\n"
                  },
                  "positive_class_segment_id": {
                    "type": "string",
                    "description": "propensity subtype only. ID of an existing segment whose members represent the positive training class for ML model training.\n"
                  },
                  "model_type": {
                    "type": "string",
                    "enum": [
                      "BOOSTED_TREE_CLASSIFIER",
                      "AUTOML_CLASSIFIER",
                      "LOGISTIC_REG"
                    ],
                    "default": "BOOSTED_TREE_CLASSIFIER",
                    "description": "propensity subtype only. BQML classifier type."
                  },
                  "excluded_columns": {
                    "type": "string",
                    "description": "propensity subtype only. Comma-prefixed feature columns excluded to prevent target leakage."
                  },
                  "model_name": {
                    "type": "string",
                    "description": "propensity subtype only. Human-readable model name."
                  },
                  "generation_metadata_id": {
                    "type": "string",
                    "description": "similarity subtype: an existing icp_id. propensity subtype: an existing model_id. When provided without seed_description/seed_segment_id (similarity) or positive_class_segment_id (propensity), attaches that already-completed run to a new segment instead of launching a new workflow — status: active immediately, no workflow_run_id.\n"
                  },
                  "visibility": {
                    "type": "string",
                    "enum": [
                      "org",
                      "private"
                    ],
                    "description": "org (default) — visible to all org members. private — visible only to creator.\n"
                  },
                  "hitl": {
                    "type": "boolean",
                    "default": false,
                    "description": "Requires an explicit confirmation step before the automated pipeline proceeds. For similarity/propensity, the workflow pauses at a human-in-the-loop review gate. For matched, the uploaded file is staged instead of landing at the path that triggers automatic identity resolution — call `POST /v1/match/{id}/analyze` to preview column mappings against the staged file, then `POST /v1/segments/{id}/mappings` to confirm (moves the file into place and starts resolution) or `POST /v1/segments/{id}/mappings/cancel` to abort (the segment is left completely untouched). Defaults to false — omit for the existing automatic-resolution behavior.\n"
                  },
                  "campaign_id": {
                    "type": "string",
                    "description": "Optional. If provided and create_audience is true, the created audience is automatically linked to this campaign.\n"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional HTTPS URL to receive segment.ready or segment.failed events. Overrides the org-level webhook URL for this request only.\n"
                  },
                  "ephemeral": {
                    "type": "boolean",
                    "default": false,
                    "description": "filter subtype only. When true, creates a scratch/free/auto-expiring segment with a short, hour-granular TTL instead of the standard 90-day expiry. Excluded from `GET /v1/segments` by default (see `include_ephemeral` there). Promote it to persistent later via `PATCH /v1/segments/{id} {ephemeral: false}`, or implicitly by composing it into an audience. Defaults to `false` (persistent, today's behavior).\n"
                  }
                }
              },
              "examples": {
                "filter": {
                  "summary": "Filter segment",
                  "value": {
                    "name": "West Coast Adults 25-44",
                    "subtype": "filter",
                    "filter_groups": [
                      {
                        "id": "g1",
                        "filters": [
                          {
                            "field": "state",
                            "op": "IN",
                            "value": [
                              "CA",
                              "OR",
                              "WA"
                            ]
                          }
                        ],
                        "combinator": "AND"
                      }
                    ],
                    "create_audience": true
                  }
                },
                "matched": {
                  "summary": "Matched segment (customer CSV upload)",
                  "value": {
                    "name": "Q3 CRM Upload",
                    "subtype": "matched",
                    "file_format": "csv",
                    "create_audience": true
                  }
                },
                "similarity": {
                  "summary": "Similarity segment (lookalike)",
                  "value": {
                    "name": "Lookalike of best customers",
                    "subtype": "similarity",
                    "seed_description": "Homeowners aged 35-55 with household income above $80k",
                    "hitl": false
                  }
                },
                "propensity": {
                  "summary": "Propensity segment (ML scoring)",
                  "value": {
                    "name": "High-Intent Refinance Prospects",
                    "subtype": "propensity",
                    "positive_class_segment_id": "seg_abc123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Segment created.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "segment_id",
                    "name",
                    "subtype",
                    "status",
                    "record_count",
                    "expires_at",
                    "ephemeral",
                    "created_at",
                    "updated_at"
                  ],
                  "properties": {
                    "segment_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "subtype": {
                      "type": "string",
                      "enum": [
                        "filter",
                        "matched",
                        "similarity",
                        "propensity"
                      ]
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "active",
                        "failed",
                        "archived",
                        "expired"
                      ]
                    },
                    "record_count": {
                      "type": [
                        "integer",
                        "null"
                      ]
                    },
                    "expires_at": {
                      "type": "string",
                      "description": "Date-only ISO string (90-day expiry) for a persistent segment; a full ISO datetime (short, hour-granular TTL) while `ephemeral` is true.\n"
                    },
                    "ephemeral": {
                      "type": "boolean",
                      "description": "True for a scratch/auto-expiring filter-subtype segment. Absent/false reads as persistent.\n"
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "updated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "upload_url": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "matched subtype only. 30-minute signed upload URL. Present when shard_count was 1 (the default) — absent when sharded, use upload_urls instead.\n"
                    },
                    "upload_urls": {
                      "type": [
                        "array",
                        "null"
                      ],
                      "items": {
                        "type": "string"
                      },
                      "description": "matched subtype only. N signed upload URLs (index-ordered). Present only when shard_count > 1 was requested — upload_url is absent. Every CSV shard must include the same header row.\n"
                    },
                    "compression": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "enum": [
                        "none",
                        "gzip",
                        null
                      ],
                      "description": "matched subtype only. Input artifact compression."
                    },
                    "upload_expires_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time",
                      "description": "matched subtype only. Expiry of upload_url/upload_urls."
                    },
                    "workflow_run_id": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "similarity/propensity subtypes. Workflow run ID for progress monitoring."
                    },
                    "audience_id": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "If create_audience was true, the ID of the auto-created audience wrapper. Null otherwise.\n"
                    }
                  }
                },
                "examples": {
                  "filter_response": {
                    "summary": "Filter segment created",
                    "value": {
                      "segment_id": "seg_abc123",
                      "name": "West Coast Adults 25-44",
                      "subtype": "filter",
                      "status": "active",
                      "record_count": null,
                      "expires_at": "2026-09-27",
                      "ephemeral": false,
                      "audience_id": "aud_abc123",
                      "created_at": "2026-06-22T18:00:00Z",
                      "updated_at": "2026-06-22T18:00:00Z"
                    }
                  },
                  "matched_response": {
                    "summary": "Matched segment created — ready for file upload",
                    "value": {
                      "segment_id": "seg_m789",
                      "name": "Q3 CRM Upload",
                      "subtype": "matched",
                      "status": "pending",
                      "record_count": null,
                      "expires_at": "2026-09-27",
                      "ephemeral": false,
                      "upload_url": "https://storage.googleapis.com/cf-uploads/enrichment-uploads/org_x/seg_m789/input.csv?X-Goog-Signature=...",
                      "upload_expires_at": "2026-06-22T19:30:00.000Z",
                      "audience_id": "aud_m789",
                      "created_at": "2026-06-22T18:00:00Z",
                      "updated_at": "2026-06-22T18:00:00Z"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "502": {
            "description": "Field Catalog temporarily unreachable — filters could not be validated (code FILTER_CATALOG_UNAVAILABLE)."
          }
        }
      },
      "get": {
        "summary": "List segments for the caller's org",
        "operationId": "listSegments",
        "description": "Returns all segments for the org, newest first. Supports filtering by subtype and status. Archived and expired segments are excluded by default unless explicitly requested. Listing emits no billable usage.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "subtype",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "filter",
                "matched",
                "similarity",
                "propensity"
              ]
            },
            "description": "Filter to a specific segment subtype.\n"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "active",
                "failed",
                "archived",
                "expired"
              ]
            },
            "description": "Filter by lifecycle status. archived/expired segments are excluded by default.\n"
          },
          {
            "name": "include_archived",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "When true, archived segments are included in results."
          },
          {
            "name": "include_ephemeral",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            },
            "description": "When true, ephemeral (scratch/auto-expiring) segments are included in results. Excluded by default.\n"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Maximum number of results to return (max 100)."
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Cursor from a previous response's `next_cursor` attribute to fetch the next page."
          }
        ],
        "responses": {
          "200": {
            "description": "Segment list.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "segments": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SegmentObject"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "description": "Pass as `cursor` on the next request. Null when no further pages exist."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/segments/{id}": {
      "get": {
        "summary": "Get a segment by ID",
        "operationId": "getSegment",
        "description": "Returns the full segment document including subtype-specific attributes, current record count, and lifecycle status. Also includes a `usage` object with `audience_count` and `campaign_count` showing how many audiences and campaigns currently reference this segment. Reading the segment emits no billable usage.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Segment document.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SegmentObject"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "summary": "Partially update a segment",
        "operationId": "patchSegment",
        "description": "Applies a partial update to a segment. Only provided attributes are updated.\n\n**Allowed updates by subtype:**\n\n- **filter** — `name`, `filters`, `filter_groups`, `status` (archived/active), `ephemeral`\n- **matched** — `name`, `column_mappings`, `status` (archived/active), `ephemeral` promotion\n- **similarity / propensity** — `name`, `status` (archived/active)\n\n**Standalone promotion:** `ephemeral: false` promotes a scratch (ephemeral) segment to persistent in-place — resets its expiry to the standard 90-day TTL and reactivates it if it had lazily expired. Only `false` is honored; `true` (demoting a persistent segment back to scratch) is not supported and is silently ignored.\n\n**Count invalidation:** updating filter criteria does NOT automatically recompute record counts on audiences referencing this segment. Call `POST /v1/segments/{id}/count` to recompute.\n\nThe update itself emits no billable usage.\n      Requires 'purchase' scope.\n\nWhen `filters`/`filter_groups` are provided, they're validated against the live Field Catalog before the update is applied — an unknown field, a disallowed operator, or an invalid enum value returns 400 `INVALID_FILTER`. If the Field Catalog is temporarily unreachable, returns 502 `FILTER_CATALOG_UNAVAILABLE` rather than applying an unvalidated update.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Rename the segment."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "archived",
                      "active"
                    ],
                    "description": "Valid user-settable transitions: archived or active (restore from archived). System-managed statuses (pending, failed, expired) cannot be set via this endpoint.\n\nArchiving is always safe and reversible: it never affects audiences that already reference this segment (see the `SegmentObject` `status` attribute docs for why). Archived segments are excluded from the default `GET /v1/segments` list (pass `?include_archived=true` to include them) and from new audience compositions, but remain fully readable via `GET /v1/segments/{id}` and can be restored with `status: active` at any time.\n"
                  },
                  "filters": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/Filter"
                    },
                    "description": "filter subtype only. Replace the filter array."
                  },
                  "filter_groups": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/FilterGroup"
                    },
                    "description": "filter subtype only. Replace filter groups."
                  },
                  "column_mappings": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "matched subtype only. Update column-to-identity-attribute mappings."
                  },
                  "ephemeral": {
                    "type": "boolean",
                    "description": "Standalone \"save this scratch segment\" promotion for any ephemeral segment subtype — see the endpoint description above. Only `false` is honored.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated segment.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SegmentObject"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      },
      "delete": {
        "summary": "Permanently remove a segment",
        "operationId": "deleteSegment",
        "description": "Removes the segment from every read surface: `GET /v1/segments/{id}` returns 404 afterward, and `GET /v1/segments` always excludes it (there is no override, unlike archived segments). There is no restore endpoint — this is a one-way operation from the API's perspective, even though the underlying record is retained internally rather than hard-deleted.\n\n**Effect on audiences:** unlike archiving, deleting a segment that's referenced by one or more audiences changes those audiences' composition — the segment is dropped from their resolved segment list and record count. This is the reason for the impact check below; archiving has no such check because it never has this effect.\n\n**Impact check:** if the segment is referenced by one or more active audiences, the server returns a 409 with `audience_count`, `campaign_count`, and an `audiences` list. Re-submit with `?confirm_impact=true` to proceed anyway.\n\nNot idempotent: calling this twice returns 404 on the second call, since the segment is already excluded from lookup after the first delete. No credits are debited.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "confirm_impact",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean"
            },
            "description": "Pass `true` to delete a segment that is referenced by active audiences. Omitting this parameter on an in-use segment returns a 409 with impact details.\n"
          }
        ],
        "responses": {
          "204": {
            "description": "Segment successfully deleted (archived).\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Segment is referenced by one or more active audiences. Response body includes impact details. Re-submit with `?confirm_impact=true` to delete anyway.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message",
                    "audience_count",
                    "campaign_count"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "SEGMENT_IN_USE"
                      ]
                    },
                    "message": {
                      "type": "string"
                    },
                    "audience_count": {
                      "type": "integer"
                    },
                    "campaign_count": {
                      "type": "integer"
                    },
                    "audiences": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "linked_campaign_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/segments/{id}/count": {
      "post": {
        "summary": "Recount a segment",
        "operationId": "countSegment",
        "description": "Triggers a record count computation for the segment. Runs a fresh computed count for every subtype: filter segments count directly against their own filter definition and update `record_count`; matched/similarity/propensity segments run the same composed count used by audience/delivery counting (a 1-membership composition), rather than echoing a possibly-stale stored count. Debits compute credits for the query.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Count result.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "segment_id",
                    "record_count"
                  ],
                  "properties": {
                    "segment_id": {
                      "type": "string"
                    },
                    "record_count": {
                      "type": [
                        "integer",
                        "null"
                      ],
                      "description": "Updated record count. Null if the segment is pending or failed."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/QueryExecutionError"
          }
        }
      }
    },
    "/v1/segments/{id}/demographics": {
      "get": {
        "summary": "Get demographic breakdown for a segment",
        "operationId": "getSegmentDemographics",
        "description": "Returns a computed demographic breakdown for the segment. This is the segment-level primary path: the breakdown is evaluated directly over the segment's own filter definition — no audience composition (set_logic or exclusions) is applied. For a composed view, use GET /v1/audiences/{id}/demographics on a referencing audience. Works for every subtype: matched, similarity, and propensity segments bridge through a materialized scratch id-set (a 1-membership composition) so the same catalog-driven breakdown logic can run against them. Response is cached server-side for 20 minutes. Requires 'discovery' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment document ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Demographic breakdown.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "segment_id",
                    "segment_name",
                    "total_count",
                    "age_breakdown",
                    "geo_breakdown",
                    "generated_at"
                  ],
                  "properties": {
                    "segment_id": {
                      "type": "string"
                    },
                    "segment_name": {
                      "type": "string"
                    },
                    "total_count": {
                      "type": "integer",
                      "description": "Total records matching the segment's filter definition."
                    },
                    "age_breakdown": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "label",
                          "pct"
                        ],
                        "properties": {
                          "label": {
                            "type": "string",
                            "description": "Age bucket label."
                          },
                          "pct": {
                            "type": "number",
                            "description": "Percentage of total_count in this bucket (0-100, rounded)."
                          }
                        }
                      }
                    },
                    "geo_breakdown": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "label",
                          "count"
                        ],
                        "properties": {
                          "label": {
                            "type": "string",
                            "description": "Geography value (e.g. state)."
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "breakdown_extra": {
                      "type": "array",
                      "description": "Present only when the query returns a behavioral or financial breakdown alongside age/geo.\n",
                      "items": {
                        "type": "object",
                        "required": [
                          "label",
                          "pct"
                        ],
                        "properties": {
                          "label": {
                            "type": "string"
                          },
                          "pct": {
                            "type": "number"
                          }
                        }
                      }
                    },
                    "extra_field": {
                      "type": "string",
                      "description": "Name of the attribute behind breakdown_extra. Present only alongside breakdown_extra.\n"
                    },
                    "generated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "segment_id": "seg_abc123",
                  "segment_name": "Sun Belt homeowners",
                  "total_count": 182400,
                  "age_breakdown": [
                    {
                      "label": "25-34",
                      "pct": 22
                    },
                    {
                      "label": "35-44",
                      "pct": 31
                    }
                  ],
                  "geo_breakdown": [
                    {
                      "label": "AZ",
                      "count": 61200
                    },
                    {
                      "label": "TX",
                      "count": 121200
                    }
                  ],
                  "generated_at": "2026-07-05T18:04:11.000Z"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/QueryExecutionError"
          }
        }
      }
    },
    "/v1/segments/{id}/audiences": {
      "get": {
        "summary": "List audiences referencing a segment",
        "operationId": "getSegmentAudiences",
        "description": "Returns all non-deleted audiences that include this segment, with campaign linkage — enables cross-campaign segment usage navigation. Requires 'discovery' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Audiences referencing this segment.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "audiences",
                    "audience_count",
                    "campaign_count"
                  ],
                  "properties": {
                    "audiences": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "linked_campaign_id",
                          "status",
                          "record_count"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "linked_campaign_id": {
                            "type": [
                              "string",
                              "null"
                            ]
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "active",
                              "expired",
                              "pending",
                              "failed",
                              "archived"
                            ]
                          },
                          "record_count": {
                            "type": [
                              "integer",
                              "null"
                            ]
                          }
                        }
                      }
                    },
                    "audience_count": {
                      "type": "integer"
                    },
                    "campaign_count": {
                      "type": "integer"
                    }
                  }
                },
                "example": {
                  "audiences": [
                    {
                      "id": "aud_abc123",
                      "name": "Sun Belt SMB Q2",
                      "linked_campaign_id": "cmp_xyz789",
                      "status": "active",
                      "record_count": 12000
                    }
                  ],
                  "audience_count": 1,
                  "campaign_count": 1
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/segments/{id}/duplicate": {
      "post": {
        "summary": "Duplicate a segment with lineage tracking",
        "operationId": "duplicateSegment",
        "description": "Creates a new segment pre-populated from the source segment. Core and subtype-specific attributes are carried; underlying computed data (match results, lookalike/propensity scores) is referenced, never recomputed. Records parent_segment_id, derived_from (root ancestor — propagates through duplicate-of-duplicate chains), and parent_segment_version for lineage. The duplicate gets fresh created_at/updated_at and a fresh 90-day expires_at. No audience wrapper is created.\n\n**Per-subtype behavior:**\n\n- **filter** — status active, record_count reset to null (recount via\n  `POST /v1/segments/{id}/count`), version history restarts at\n  `current_version: 1`.\n\n\n- **matched** — record_count and match_count carried. The duplicate\n  references the source's stored match results, so it stays deliverable\n  without re-running the match. Sources with expired/failed status or incomplete match data\n  are rejected with 409 SOURCE_MATCH_UNAVAILABLE.\n\n\n- **similarity/propensity** — seed/model references and record_count\n  carried; the duplicate points at the same generated scores.\n\n\nPending or failed sources are rejected with 409 SOURCE_NOT_READY.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Source segment document ID.\n"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Display name for the duplicate. Defaults to \"<source name> (copy)\".\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Duplicated segment created.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SegmentObject"
                },
                "example": {
                  "segment_id": "seg_dup456",
                  "name": "West Coast Adults 25-44 (copy)",
                  "subtype": "filter",
                  "status": "active",
                  "record_count": null,
                  "expires_at": "2026-10-03",
                  "ephemeral": false,
                  "visibility": "org",
                  "current_version": 1,
                  "filters": [],
                  "filter_groups": [
                    {
                      "id": "g1",
                      "filters": [
                        {
                          "field": "state",
                          "op": "IN",
                          "value": [
                            "CA",
                            "OR",
                            "WA"
                          ]
                        }
                      ],
                      "combinator": "AND"
                    }
                  ],
                  "parent_segment_id": "seg_abc123",
                  "derived_from": "seg_abc123",
                  "parent_segment_version": 3,
                  "created_at": "2026-07-05T18:00:00Z",
                  "updated_at": "2026-07-05T18:00:00Z",
                  "integration_provenance": null,
                  "integration_collection_provenance": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The source segment cannot be duplicated. SOURCE_MATCH_UNAVAILABLE — matched source whose status is pending/expired/failed or whose matching_status is incomplete (its match data cannot be referenced). SOURCE_NOT_READY — non-matched source with status pending or failed.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "SOURCE_MATCH_UNAVAILABLE",
                        "SOURCE_NOT_READY"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "Conflict",
                  "code": "SOURCE_MATCH_UNAVAILABLE",
                  "message": "The source segment's match data is unavailable (status 'expired'). Only matched segments with completed match data can be duplicated."
                }
              }
            }
          }
        }
      }
    },
    "/v1/segments/bulk": {
      "patch": {
        "summary": "Bulk update segments",
        "operationId": "bulkPatchSegments",
        "description": "Apply the same name and/or status change to up to 50 segments in one request. Only name and status are bulk-patchable — they are valid across all four subtypes. Composition and subtype-specific attributes (filters, filter_groups, column_mappings) are rejected with 400 INVALID_FIELD; use `PATCH /v1/segments/{id}` for those. Best-effort per segment: each segment is updated independently and failures (not found, invalid status transition) are reported per id in the `failed` array — successful updates are never rolled back. The single-PATCH status transition rule applies per segment: only archived segments can be restored to active. Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "segment_ids"
                ],
                "properties": {
                  "segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 50,
                    "description": "IDs of segments to update (1-50)."
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "New display name applied to every listed segment."
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "active",
                      "archived"
                    ],
                    "description": "New status applied to every listed segment. Only user-settable values are accepted; 'active' is only valid on segments currently archived (per-segment failure otherwise).\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bulk update processed. Per-segment failures, if any, are listed in `failed`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "updated",
                    "failed"
                  ],
                  "properties": {
                    "updated": {
                      "type": "integer",
                      "description": "Number of segments successfully updated."
                    },
                    "failed": {
                      "type": "array",
                      "description": "Segments that could not be updated.",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "error"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "updated": 2,
                  "failed": [
                    {
                      "id": "seg_missing",
                      "error": "Not found"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "delete": {
        "summary": "Bulk archive segments",
        "operationId": "bulkArchiveSegments",
        "description": "Archive up to 50 segments in one request by setting `status: archived`. Deliberately DIVERGES from `DELETE /v1/segments/{id}`: the single-segment DELETE permanently removes the segment (sets `deleted: true` behind a SEGMENT_IN_USE impact gate, dropping it out of every referencing audience's composition), while bulk archive only changes status — archived segments remain fully resolvable by audiences that reference them, so composition integrity is preserved and no impact gate is needed. Fully reversible via `PATCH {status: active}` (single or bulk). Best-effort per segment: not-found/deleted ids are reported in the `failed` array; already-archived segments count as success (idempotent). Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "segment_ids"
                ],
                "properties": {
                  "segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 50,
                    "description": "IDs of segments to archive (1-50)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bulk archive processed. Per-segment failures, if any, are listed in `failed`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "archived",
                    "failed"
                  ],
                  "properties": {
                    "archived": {
                      "type": "integer",
                      "description": "Number of segments now archived (includes already-archived segments, which are idempotent successes).\n"
                    },
                    "failed": {
                      "type": "array",
                      "description": "Segments that could not be archived.",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "error"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "archived": 3,
                  "failed": [
                    {
                      "id": "seg_missing",
                      "error": "Not found"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/segments/bulk-delete": {
      "delete": {
        "summary": "Bulk hard-delete segments",
        "operationId": "bulkDeleteSegments",
        "description": "Permanently delete up to 50 segments in one request — the bulk counterpart of `DELETE /v1/segments/{id}` (sets `deleted: true`, dropping each segment out of every referencing audience's composition). Unlike the single-segment DELETE, there is no `confirm_impact` bypass here: a segment referenced by 1+ audiences is always skipped (never force-deleted) and reported back in `skipped`, since a bulk action has no per-segment confirmation UI to make an informed override decision. Delete the segment individually via `DELETE /v1/segments/{id}?confirm_impact=true` to force it. Best-effort per segment: not-found/already-deleted ids are reported in `failed`. Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "segment_ids"
                ],
                "properties": {
                  "segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 50,
                    "description": "IDs of segments to delete (1-50)."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Bulk delete processed. In-use segments and per-segment failures, if any, are listed in `skipped`/`failed` respectively.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "deleted",
                    "skipped",
                    "failed"
                  ],
                  "properties": {
                    "deleted": {
                      "type": "integer",
                      "description": "Number of segments permanently deleted."
                    },
                    "skipped": {
                      "type": "array",
                      "description": "Segments left untouched because they're referenced by 1+ audiences.",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "audience_count",
                          "campaign_count"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "audience_count": {
                            "type": "integer"
                          },
                          "campaign_count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "failed": {
                      "type": "array",
                      "description": "Segments that could not be deleted (not found/already deleted).",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "error"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "deleted": 1,
                  "skipped": [
                    {
                      "id": "seg_in_use",
                      "audience_count": 2,
                      "campaign_count": 1
                    }
                  ],
                  "failed": [
                    {
                      "id": "seg_missing",
                      "error": "Not found"
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/segments/{id}/refresh": {
      "post": {
        "summary": "Refresh a segment against the latest data",
        "operationId": "refreshSegment",
        "description": "Re-derives a single segment from the latest identity graph data. This is the segment-native counterpart of `POST /v1/audiences/{id}/refresh` — it updates ONLY this segment. Referencing audiences are never touched; their IDs are returned in `affected_audiences` so you can recount or refresh them as needed (use the audience-level refresh for a whole-composition refresh).\n\n**Per-subtype behavior:**\n\n- **filter** — Synchronous. Bumps `current_version` and appends a\n  version history entry (sharing the same counter as PATCH filter\n  edits), clears `record_count`, resets the 90-day TTL, and returns\n  `status: active`. If nothing would change (already active, count\n  already null, TTL already fresh today) and `force` is not set, the\n  call is a no-op returning the current segment with\n  `refreshed: false`.\n\n\n- **matched** — Returns a fresh presigned `upload_url` (24 h expiry) — or\n  `upload_urls` (an array, one per shard) when `shard_count` > 1,\n  in which case `upload_url` is absent. The new upload may be raw or\n  gzip. With `hitl`\n  unset/false (default): targets the segment's existing upload path\n  directly, resets mapping state (`column_mappings`,\n  `mapping_confirmed`) and counts, and returns `status: pending` —\n  re-upload your identity list and identity resolution runs\n  automatically once every declared shard has been uploaded. With\n  `hitl: true`: the URL(s) instead target a staging location and the\n  segment's status/mapping state is left untouched until you confirm\n  — see the `hitl` field description below. Matched duplicates that\n  share another segment's data are rejected with 409\n  SHARED_SOURCE_DATA — refresh the source segment instead. A saved\n  integration-match segment owns its own lifecycle and remains refreshable.\n\n\n- **similarity** — Re-launches the lookalike generation workflow with\n  the stored seed parameters. Returns `status: pending`; the segment's\n  `workflow_run_id` is updated to the new run. Poll\n  `GET /v1/workflows/{workflow_run_id}` until completion.\n\n\n- **propensity** — Re-launches the scoring model workflow using the\n  stored `positive_class_segment_id`. Returns `status: pending` plus\n  the new `workflow_run_id`. 422 MISSING_POSITIVE_CLASS_ID for legacy\n  segments that predate `positive_class_segment_id` storage — those\n  must be recreated.\n\n\nArchived segments are rejected with 409 SEGMENT_ARCHIVED — restore first via `PATCH /v1/segments/{id}` with `status: active`.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment document ID.\n"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "hitl": {
                    "type": "boolean",
                    "default": false,
                    "description": "Requires an explicit confirmation step before the automated pipeline proceeds. **(similarity / propensity)** the re-launched workflow pauses at a human-in-the-loop review gate — use `POST /v1/workflows/{workflow_run_id}/resume` to submit answers and resume. **(matched)** the re-upload is staged instead of landing at the path that triggers automatic identity resolution — call `POST /v1/match/{id}/analyze` to preview column mappings against the staged file, then `POST /v1/segments/{id}/mappings` to confirm (moves the file into place and starts resolution) or `POST /v1/segments/{id}/mappings/cancel` to abort (the segment is left completely untouched). Ignored for filter.\n"
                  },
                  "force": {
                    "type": "boolean",
                    "default": false,
                    "description": "**(filter only)** Bump the version and invalidate the count even when the segment already looks fresh (skips the no-op short-circuit).\n"
                  },
                  "shard_count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 1,
                    "description": "**(matched only)** Set > 1 for same-format shards. Every CSV shard must include the same header, and all shards must share schema and compression. Returns `upload_urls` instead of `upload_url`.\n"
                  },
                  "compression": {
                    "type": "string",
                    "enum": [
                      "none",
                      "gzip"
                    ],
                    "default": "none",
                    "description": "**(matched only)** Compression for this new upload. It does not inherit the previous upload's setting. Send raw gzip bytes without Content-Encoding. gzip is not supported for avro-format segments — rejected with 400 UNSUPPORTED_INPUT_COMPRESSION.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Segment refreshed (or no-op — inspect `refreshed`). Shape varies by subtype: matched adds `upload_url`/`upload_expires_at` (or `upload_urls` when `shard_count` > 1); similarity/propensity carry the new `workflow_run_id`. See `next_step` for a machine-readable hint.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SegmentRefreshResult"
                },
                "example": {
                  "segment_id": "seg_abc123",
                  "name": "West Coast Adults 25-44",
                  "subtype": "filter",
                  "status": "active",
                  "record_count": null,
                  "expires_at": "2026-10-03",
                  "ephemeral": false,
                  "visibility": "org",
                  "current_version": 4,
                  "filters": [],
                  "filter_groups": [
                    {
                      "id": "g1",
                      "filters": [
                        {
                          "field": "state",
                          "op": "IN",
                          "value": [
                            "CA",
                            "OR",
                            "WA"
                          ]
                        }
                      ],
                      "combinator": "AND"
                    }
                  ],
                  "created_at": "2026-04-01T12:00:00Z",
                  "updated_at": "2026-07-05T18:00:00Z",
                  "refreshed": true,
                  "affected_audiences": [
                    "aud_123",
                    "aud_456"
                  ],
                  "next_step": "Call POST /v1/segments/{id}/count (recount) to refresh the record count. Referencing audiences are NOT auto-refreshed — recount or refresh the audiences listed in affected_audiences as needed.",
                  "integration_provenance": null,
                  "integration_collection_provenance": null
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "The segment cannot be refreshed. SEGMENT_ARCHIVED — the segment is archived; restore it first (PATCH `status: active`). SHARED_SOURCE_DATA — matched duplicate that references another segment's match data; refreshing it here would overwrite the source segment's upload. The message names the source segment id to refresh instead.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "SEGMENT_ARCHIVED",
                        "SHARED_SOURCE_DATA"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "Conflict",
                  "code": "SHARED_SOURCE_DATA",
                  "message": "This matched segment is a duplicate that references segment 'seg_root1' for its match data — refreshing it here would overwrite that source segment's upload. Refresh the source segment instead: POST /v1/segments/seg_root1/refresh."
                }
              }
            }
          },
          "422": {
            "description": "MISSING_POSITIVE_CLASS_ID — **(propensity only)** the segment predates `positive_class_segment_id` storage and cannot be auto-refreshed; delete and recreate it.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "MISSING_POSITIVE_CLASS_ID"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/segments/{id}/mappings": {
      "post": {
        "summary": "Confirm column mapping overrides for a matched segment",
        "operationId": "confirmSegmentMappings",
        "description": "Stores confirmed column mapping overrides for a matched-subtype segment. Call this after reviewing the `POST /v1/match/{id}/analyze` response to correct any incorrect predictions. Sets `mapping_confirmed: true` so the platform skips its own analysis and uses these mappings directly.\n\n`column_mappings` may be omitted entirely — the platform then re-runs column analysis on the currently active upload itself and accepts its own suggested mapping as-is (the same behavior as reviewing `POST /v1/match/{id}/analyze` and confirming without edits, in one call). This can return 409 `FILE_NOT_YET_UPLOADED` if the file hasn't finished uploading yet, or 422/400 on other analysis failures — pass `column_mappings` explicitly to skip re-analysis entirely.\n\nIf the segment was created or refreshed with `hitl: true`, the upload is still staged (not yet at the path that triggers automatic resolution) — this call also moves the staged file into place and starts identity resolution, which is why it can return 500 `STAGING_COPY_FAILED` if that move fails (the segment is left unchanged in that case; retry the call). Use `POST /v1/segments/{id}/mappings/cancel` instead to abort a staged upload without confirming.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment document ID."
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "column_mappings": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Map of source column name → standard identity attribute name. Use `\"(skip)\"` as the value to explicitly exclude a column. Omit entirely (send `{}` or no body) to accept the platform's own fresh column analysis of the active upload as-is.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mappings saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "column_mappings"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "column_mappings": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "description": "STAGING_COPY_FAILED — moving a staged (`hitl: true`) upload into place failed; the segment is unchanged, retry the call. ANALYSIS_FAILED — `column_mappings` was omitted and re-analysis failed unexpectedly.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "STAGING_COPY_FAILED",
                        "ANALYSIS_FAILED"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/segments/{id}/mappings/cancel": {
      "post": {
        "summary": "Abort a staged (hitl:true) matched-segment upload without confirming",
        "operationId": "cancelSegmentMappings",
        "description": "Clears the staged upload created by a `hitl: true` call to `POST /v1/segments` or `POST /v1/segments/{id}/refresh`. Performs no file move and no status/mapping-state change — the segment is left exactly as it was before the upload was initiated. Idempotent: returns 200 even if nothing is currently staged.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment document ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Staged upload cleared (or already clear).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/segments/{id}/upload-failed": {
      "post": {
        "summary": "Report that a matched segment's client-side re-upload failed",
        "operationId": "reportSegmentUploadFailed",
        "description": "Called when a matched segment's re-upload (kicked off by `POST /v1/segments/{id}/refresh` or `POST /v1/audiences/{id}/refresh`) fails client-side before ever reaching storage — e.g. a browser-side network or CORS failure on the presigned upload PUT. The async pipeline that would normally fail the segment never starts in that case, since no upload ever arrived, so without this call the segment (and any linked audience) would otherwise stay `pending` until a much slower backstop eventually catches it.\n\nIdempotent — a no-op (`already_resolved: true`) if the segment has already moved past `pending` (e.g. the upload actually succeeded). Only valid for matched-subtype segments.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Segment document ID.\n"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "audience_id": {
                    "type": "string",
                    "description": "Optional linked audience to also mark failed, in addition to the segment's own linked audience (if any).\n"
                  },
                  "reason": {
                    "type": "string",
                    "maxLength": 500,
                    "description": "Human-readable failure reason."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Segment (and any linked audience) marked failed, or already resolved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "already_resolved"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "already_resolved": {
                      "type": "boolean",
                      "description": "True if the segment had already moved past pending before this call."
                    }
                  }
                },
                "example": {
                  "ok": true,
                  "already_resolved": false
                }
              }
            }
          },
          "400": {
            "description": "NOT_A_MATCHED_UPLOAD — only matched-subtype segments have an upload to report as failed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "NOT_A_MATCHED_UPLOAD"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "Bad Request",
                  "code": "NOT_A_MATCHED_UPLOAD",
                  "message": "Only matched-subtype segments have an upload to report as failed."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/audiences": {
      "post": {
        "summary": "Create an audience",
        "operationId": "createAudience",
        "description": "Creates a named audience — a subtype-agnostic composition wrapper that references one or more segments (include or exclude role) for delivery.\n\nAudiences are always `status: active` immediately on creation. The underlying segments determine data readiness — only deliver an audience after all its include-role segments are active.\n\n**Composition options:**\n\n- Use `segment_refs` (preferred) to specify each segment and its role\n  (`include` or `exclude`) explicitly.\n\n- Use `segment_ids` / `excluded_segment_ids` (convenience shorthand)\n  as a flat list when all included segments share the same include role.\n\n- Use `set_logic` to control how multiple include-role segments are\n  combined: `union` (default) — a record matches ANY included segment (OR) —\n  or `intersection` — a record must match EVERY included segment (AND).\n\n- Excluded segments are always subtracted from the composed include\n  result (AND NOT), regardless of `set_logic`:\n  result = (included segments combined by set_logic) MINUS (excluded segments).\n\n\nTo create matched, similarity, or propensity segments, use `POST /v1/segments`.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Display name for the audience.\n"
                  },
                  "segment_refs": {
                    "type": "array",
                    "description": "Preferred composition format. Each entry specifies a segment and its role. Supersedes `segment_ids` / `excluded_segment_ids` if provided.\n",
                    "items": {
                      "type": "object",
                      "required": [
                        "segment_id",
                        "role"
                      ],
                      "properties": {
                        "segment_id": {
                          "type": "string"
                        },
                        "role": {
                          "type": "string",
                          "enum": [
                            "include",
                            "exclude"
                          ]
                        }
                      }
                    }
                  },
                  "segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Convenience shorthand: IDs of segments to include (role: include). Use segment_refs for mixed include/exclude compositions.\n"
                  },
                  "excluded_segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Convenience shorthand: IDs of segments to exclude from the composition.\n"
                  },
                  "set_logic": {
                    "type": "string",
                    "enum": [
                      "union",
                      "intersection"
                    ],
                    "default": "union",
                    "description": "How to combine the include-role segments. Exclusions always apply as AND NOT.\n"
                  },
                  "visibility": {
                    "type": "string",
                    "enum": [
                      "org",
                      "private"
                    ],
                    "description": "org (default) — visible to all members of the org. private — visible only to the creating user.\n"
                  },
                  "campaign_id": {
                    "type": "string",
                    "description": "Optional. If provided, the created audience is automatically linked to this campaign. The audience can belong to at most one campaign at a time. Omit if you are not using campaign workspaces.\n"
                  }
                }
              },
              "examples": {
                "segment_refs": {
                  "summary": "Audience with explicit segment roles",
                  "value": {
                    "name": "West Coast Adults 25-44",
                    "segment_refs": [
                      {
                        "segment_id": "seg_abc123",
                        "role": "include"
                      },
                      {
                        "segment_id": "seg_def456",
                        "role": "include"
                      },
                      {
                        "segment_id": "seg_sup789",
                        "role": "exclude"
                      }
                    ],
                    "set_logic": "union"
                  }
                },
                "shorthand": {
                  "summary": "Audience using shorthand segment_ids",
                  "value": {
                    "name": "Q3 CRM Audience",
                    "segment_ids": [
                      "seg_abc123",
                      "seg_def456"
                    ],
                    "set_logic": "union"
                  }
                },
                "with_campaign": {
                  "summary": "Audience linked to a campaign",
                  "value": {
                    "name": "Q3 West Coast Adults",
                    "segment_ids": [
                      "seg_abc123"
                    ],
                    "campaign_id": "camp_abc123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Audience created.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceObject"
                },
                "examples": {
                  "created": {
                    "summary": "Audience created",
                    "value": {
                      "audience_id": "aud_abc123",
                      "name": "West Coast Adults 25-44",
                      "status": "active",
                      "version": 1,
                      "record_count": null,
                      "expires_at": "2026-09-27",
                      "visibility": "org",
                      "segment_refs": [
                        {
                          "segment_id": "seg_abc123",
                          "role": "include"
                        },
                        {
                          "segment_id": "seg_def456",
                          "role": "include"
                        }
                      ],
                      "segment_ids": [
                        "seg_abc123",
                        "seg_def456"
                      ],
                      "excluded_segment_ids": [],
                      "segments": [
                        {
                          "id": "seg_abc123",
                          "name": "West Coast States",
                          "record_count": 1204331,
                          "subtype": "filter",
                          "input_record_count": null,
                          "match_count": null
                        },
                        {
                          "id": "seg_def456",
                          "name": "Adults 25-44",
                          "record_count": 8443210,
                          "subtype": "filter",
                          "input_record_count": null,
                          "match_count": null
                        }
                      ],
                      "excluded_segments": [],
                      "set_logic": "union",
                      "created_at": "2026-06-22T18:00:00Z",
                      "updated_at": "2026-06-22T18:00:00Z",
                      "integration_provenance": null
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "get": {
        "summary": "List audiences for the caller's org",
        "operationId": "listAudiences",
        "description": "Returns all non-deleted audiences for the org, newest first. Each row includes expanded `segments` / `excluded_segments` summaries (id, name, record_count, subtype) for its constituent segments — the same expansion as `GET /v1/audiences/{id}`. Requires 'discovery' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "active",
                "expired",
                "archived"
              ]
            },
            "description": "Filter by audience status. `archived` audiences are excluded by default; pass `status=archived` to retrieve them explicitly.\n"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            },
            "description": "Maximum number of results to return.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Audience list.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "audiences": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AudienceObject"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/audiences/{id}": {
      "get": {
        "summary": "Get an audience by ID",
        "operationId": "getAudience",
        "description": "Returns the audience with expanded segment metadata — both include-role (`segments`) and exclude-role (`excluded_segments`) summaries, each carrying the segment's `subtype`. Requires 'discovery' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Audience document with expanded segment metadata.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceObject"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "put": {
        "summary": "Update an audience",
        "operationId": "updateAudience",
        "description": "Fully replaces the audience's `name`, `segment_ids`, `excluded_segment_ids`, and `set_logic` with the provided values. Audiences are subtype-agnostic composition wrappers — to change which segments are composed, update the segment ID lists here.\n\n**Composition model:** `set_logic: union` combines included segments with OR (match ANY); `intersection` combines them with AND (match EVERY). Segments in `excluded_segment_ids` are always subtracted from that result (AND NOT), regardless of `set_logic`.\n\n**Count invalidation:** changing the segment composition **clears the cached `record_count`**. Call `POST /v1/audiences/{id}/count` after updating to recompute it. The `record_count` will be `null` until recomputed.\n\n**Versioning:** the audience `version` is incremented on every successful PUT. Previously completed deliveries at prior versions remain accessible in delivery history.\n\nThe update itself emits no billable usage.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Full replacement list of included segment IDs.\n"
                  },
                  "excluded_segment_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Full replacement list of excluded segment IDs.\n"
                  },
                  "set_logic": {
                    "type": "string",
                    "enum": [
                      "union",
                      "intersection"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated audience.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceObject"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "patch": {
        "summary": "Partially update an audience",
        "operationId": "patchAudience",
        "description": "Applies a partial update to an audience. Accepts any subset of the attributes below — only provided attributes are updated. Use `add_segment_id` / `remove_segment_id` for incremental segment edits (preferred over replacing the full `segment_ids` array). Use `name` to rename.\n\n**Composition model:** `set_logic: union` combines included segments with OR (match ANY); `intersection` combines them with AND (match EVERY). Excluded segments (`add_excluded_segment_id`) are always subtracted from that result (AND NOT), regardless of `set_logic`.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "description": "Rename the audience.\n"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "archived",
                      "active"
                    ],
                    "description": "Valid user-settable transitions: `archived` — soft-deletes the audience; excluded from list results by default. `active` — restores a previously archived audience back to active; **rejected with 400 (`INVALID_STATUS_TRANSITION`) if the current status is not `archived`**. System-managed statuses (`pending`, `expired`, `failed`) cannot be set via this endpoint and will be rejected with 400.\n"
                  },
                  "set_logic": {
                    "type": "string",
                    "enum": [
                      "union",
                      "intersection"
                    ]
                  },
                  "add_segment_id": {
                    "type": "string",
                    "description": "Append a single segment ID to the included list.\n"
                  },
                  "remove_segment_id": {
                    "type": "string",
                    "description": "Remove a single segment ID from the included list.\n"
                  },
                  "add_excluded_segment_id": {
                    "type": "string",
                    "description": "Append a single segment ID to the excluded list.\n"
                  },
                  "remove_excluded_segment_id": {
                    "type": "string",
                    "description": "Remove a single segment ID from the excluded list.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated audience.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceObject"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "delete": {
        "summary": "Delete an audience",
        "operationId": "deleteAudience",
        "description": "Soft-deletes the audience by marking it as archived. The audience is excluded from `GET /v1/audiences` list responses and cannot be used for new deliveries, but its delivery history remains accessible via `GET /v1/audiences/{id}/deliveries`.\n\n**In-flight deliveries** that are already processing are not cancelled — they will complete normally. New delivery requests against an archived audience are rejected with `404`.\n\nTo restore an archived audience, use `PATCH /v1/audiences/{id}` with `{ \"status\": \"active\" }`. Idempotent — deleting an already-archived audience returns `204` without error. The archive action emits no billable usage.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted.\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/audiences/{id}/duplicate": {
      "post": {
        "summary": "Duplicate an audience with lineage tracking",
        "operationId": "duplicateAudience",
        "description": "Duplicates an audience with lineage tracking. Preserves composition (segment_ids, excluded_segment_ids, set_logic) and metadata. Records parent_audience_id, derived_from, and the source's segment version snapshot for lineage. Resets record_count to null, forcing a fresh recount. Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Source audience document ID.\n"
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Display name for the duplicate. Defaults to \"<source name> (copy)\".\n"
                  },
                  "campaign_id": {
                    "type": "string",
                    "description": "Optional — link the duplicate into this campaign on creation.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Duplicated audience created.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceObject"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/audiences/{id}/count": {
      "post": {
        "summary": "Recompute record count for an audience",
        "operationId": "recountAudience",
        "description": "Recomputes the record count for a saved audience based on its current composition — included segments combined by `set_logic` (`union` = OR, `intersection` = AND) minus any `excluded_segment_ids` (AND NOT). Call after adding or removing segments. Updates `audience.record_count`.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience document ID.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Count computed.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "audience_id": {
                      "type": "string"
                    },
                    "record_count": {
                      "type": "integer"
                    },
                    "matched_record_count": {
                      "type": "integer",
                      "description": "Records contributed by matched-subtype segments in this audience's composition. Present only when the composition includes at least one included matched segment; absent for pure-filter or non-matched compositions.\n"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/QueryExecutionError"
          }
        }
      }
    },
    "/v1/audiences/{id}/demographics": {
      "get": {
        "summary": "Get demographic breakdown for an audience",
        "operationId": "getAudienceDemographics",
        "description": "Returns a computed demographic breakdown for the audience, evaluated over the audience as a composition: its include-role segments combined per set_logic, minus any excluded segments. Works for every composition — pure-filter, a single non-filter subtype (matched, similarity, propensity), or a genuine mix — via a materialized scratch id-set bridge for compositions that include a non-filter segment. Response is cached server-side for 20 minutes.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience document ID.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Demographic breakdown.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "audience_id": {
                      "type": "string"
                    },
                    "record_count": {
                      "type": "integer"
                    },
                    "demographics": {
                      "type": "object",
                      "properties": {
                        "median_age": {
                          "type": "number"
                        },
                        "female_pct": {
                          "type": "number"
                        },
                        "homeowner_pct": {
                          "type": "number"
                        },
                        "children_pct": {
                          "type": "number"
                        },
                        "college_pct": {
                          "type": "number"
                        },
                        "high_income_pct": {
                          "type": "number"
                        }
                      }
                    },
                    "generated_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/QueryExecutionError"
          }
        }
      }
    },
    "/v1/match/{id}/analyze": {
      "post": {
        "summary": "Analyze matched segment column mapping",
        "operationId": "analyzeMatchSegment",
        "description": "Streams a bounded schema sample from csv/json/jsonl/Avro input (raw or gzip), analyzes the column structure using AI inference, and returns a mapping of your source columns to standard identity attributes. Takes a **segment_id** (returned by `POST /v1/match/file`) — no audience wrapper required. Call this after uploading your file to preview how columns will be interpreted before the match runs.\n\n**Response attributes to watch:**\n- `columns[].confidence`: values below 0.6 indicate uncertain mappings that are\n  worth verifying before the match runs.\n\n- `columns[].warning`: an attribute-level concern flagged by the analyzer (e.g. ambiguous\n  column name or missing country codes on phone numbers).\n\n- `unmapped_columns`: columns that could not be mapped and will be excluded from\n  identity resolution. If a critical identity attribute (email, phone, last_name) appears\n  here, the match will have reduced accuracy or may fail entirely.\n\n- `recommendations`: suggestions for improving match quality.\n\n**If mappings are incorrect:** rename the affected columns in your file to match standard identity attribute names (`email`, `phone`, `email_sha256`, `phone_sha256`, `first_name`, `middle_name`, `last_name`, `name_suffix`, `address_1`, `address_2`, `city`, `state`, `zip`, `dob`, `iag_person_id`) and re-upload. The match workflow will re-analyze automatically.\n\n**Analysis gate:** unless the segment was created/refreshed with `hitl: true`, the platform always runs its own column analysis automatically as part of the match workflow, and by the time this endpoint can return real data the upload has already triggered that automatic resolution — calling it does not pause or gate anything in that case, it's a read-only preview only. If no usable identity attributes can be resolved, the segment transitions to `failed` and `error_message` describes which columns could not be mapped.\n\n**With `hitl: true`:** the segment's upload is staged rather than resolving automatically, so this endpoint reads the staged file and genuinely nothing has started yet — review the response, then call `POST /v1/segments/{id}/mappings` to confirm (starts resolution) or `POST /v1/segments/{id}/mappings/cancel` to abort.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Enrichment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The segment_id of the matched segment (from POST /v1/match/file)."
          }
        ],
        "responses": {
          "200": {
            "description": "Column mapping analysis.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "segment_id",
                    "analysis"
                  ],
                  "properties": {
                    "segment_id": {
                      "type": "string"
                    },
                    "analysis": {
                      "type": "object",
                      "required": [
                        "columns",
                        "unmapped_columns",
                        "recommendations"
                      ],
                      "properties": {
                        "columns": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "required": [
                              "source_column",
                              "mapped_to",
                              "inferred_type",
                              "confidence",
                              "warning"
                            ],
                            "properties": {
                              "source_column": {
                                "type": "string"
                              },
                              "mapped_to": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              },
                              "inferred_type": {
                                "type": "string"
                              },
                              "confidence": {
                                "type": "number"
                              },
                              "warning": {
                                "type": [
                                  "string",
                                  "null"
                                ]
                              }
                            }
                          }
                        },
                        "unmapped_columns": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "recommendations": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "segment_id": "seg_abc123",
                  "analysis": {
                    "columns": [
                      {
                        "source_column": "Email Address",
                        "mapped_to": "email",
                        "inferred_type": "STRING",
                        "confidence": 0.98,
                        "warning": null
                      },
                      {
                        "source_column": "Mobile",
                        "mapped_to": "phone",
                        "inferred_type": "STRING",
                        "confidence": 0.84,
                        "warning": "Phone numbers lack country codes — US assumed"
                      }
                    ],
                    "unmapped_columns": [],
                    "recommendations": [
                      "Add country codes to phone numbers for higher match rates"
                    ]
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "500": {
            "$ref": "#/components/responses/InternalServerError"
          }
        }
      }
    },
    "/v1/audiences/{id}/refresh": {
      "post": {
        "summary": "Refresh a member segment and this audience's composed metadata",
        "operationId": "refreshAudience",
        "description": "Re-derives one member segment of this audience from the latest identity graph data, then bumps only this audience's own composed metadata (`snapshot_version`/`expires_at`/`status`; `record_count` is cleared and must be re-requested via `POST /v1/audiences/{id}/count`).\nAn audience is a composition of N member segments (possibly mixed subtypes) — there is no \"primary\" segment. You must name exactly which member segment to refresh via the required `segment_id` body field (list an audience's segments via `GET /v1/audiences/{id}`). That segment's refresh delegates entirely to the same implementation `POST /v1/segments/{id}/refresh` uses, so behavior for the named segment is identical either way. This route never touches any OTHER segment in the composition, and — since segment refresh is deliberately non-cascading — never touches any OTHER audience that happens to reference the same segment.\nBehavior is driven by the refreshed segment's subtype:\n\n**filter** — Synchronous refresh. The audience is immediately returned as `status: active` with an incremented version number and a new 90-day `expires_at`. The `record_count` is cleared and must be re-requested via `POST /v1/audiences/{id}/count`.\n\n**matched** — Returns a fresh presigned `upload_url` (24 h expiry) — or `upload_urls` (an array, one per shard) when `shard_count` > 1, in which case `upload_url` is absent. The new upload may be raw or gzip. With `hitl` unset/false (default): returns `status: pending` immediately and the URL(s) target the canonical upload path — re-upload your identity list and identity resolution runs automatically once every declared shard has been uploaded. With `hitl: true`: the segment's status is left untouched and the URL(s) instead target a staging location — call `POST /v1/match/{segment_id}/analyze` to preview column mappings, then `POST /v1/audiences/{id}/mappings` to confirm (starts resolution) or `POST /v1/audiences/{id}/mappings/cancel` to abort.\n\n**similarity/propensity** — Re-launches the generation workflow. Returns `status: pending` plus a `workflow_run_id`; poll `GET /v1/workflows/{workflow_run_id}` until completion.\n\n**Important:** Delivery requests are rejected with 422 while constituent segments are still being processed.\n\nTo refresh a single segment without touching its audience wrappers, use `POST /v1/segments/{id}/refresh` — it updates only that segment and returns `affected_audiences` for follow-up.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience document ID.\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "segment_id"
                ],
                "properties": {
                  "segment_id": {
                    "type": "string",
                    "description": "Required. ID of the member segment (within this audience's composition) to refresh. Rejected with 400 `SEGMENT_ID_REQUIRED` if omitted, or 400 `SEGMENT_NOT_A_MEMBER` if the segment is not a member of this audience. `GET /v1/audiences/{id}` to list this audience's segments.\n"
                  },
                  "hitl": {
                    "type": "boolean",
                    "default": false,
                    "description": "Requires an explicit confirmation step before the automated pipeline proceeds. **(similarity / propensity)** the refresh workflow pauses at a human-in-the-loop review gate — use `POST /v1/workflows/{workflow_run_id}/resume` with `gate_id` and `response` to submit answers and resume. **(matched)** the re-upload is staged instead of landing at the path that triggers automatic identity resolution — see the `matched` bullet above. Ignored for filter. Default: `false` (fully automated).\n"
                  },
                  "shard_count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 1,
                    "description": "**(matched only)** Set > 1 for same-format shards. Every CSV shard must include the same header, and all shards must share schema and compression. Returns `upload_urls` instead of `upload_url`.\n"
                  },
                  "compression": {
                    "type": "string",
                    "enum": [
                      "none",
                      "gzip"
                    ],
                    "default": "none",
                    "description": "**(matched only)** Compression for this new upload. It does not inherit the previous upload. Send raw gzip bytes without Content-Encoding. gzip is not supported for avro-format segments — rejected with 400 UNSUPPORTED_INPUT_COMPRESSION.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Audience refreshed. Inspect `status` to determine next steps. See `next_step` for a machine-readable hint.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AudienceRefreshResult"
                },
                "example": {
                  "audience_id": "aud_xyz123",
                  "segment_id": "seg_abc123",
                  "version": 2,
                  "version_created_at": "2026-06-30T14:00:00Z",
                  "expires_at": "2026-09-28T14:00:00Z",
                  "status": "active",
                  "subtype": "filter",
                  "refreshed": true,
                  "next_step": "Call recount_audience to refresh the record count before quoting or delivering."
                }
              }
            }
          },
          "400": {
            "description": "`SEGMENT_ID_REQUIRED` — `segment_id` was omitted from the request body. `SEGMENT_NOT_A_MEMBER` — the given `segment_id` is not a member of this audience. `UNKNOWN_SUBTYPE` — the segment has an unrecognized subtype and cannot be refreshed. `UNSUPPORTED_INPUT_COMPRESSION` — `compression=gzip` was requested for an avro-format matched member segment.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "SEGMENT_ID_REQUIRED",
                        "SEGMENT_NOT_A_MEMBER",
                        "UNKNOWN_SUBTYPE",
                        "UNSUPPORTED_INPUT_COMPRESSION"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                },
                "example": {
                  "error": "Bad Request",
                  "code": "SEGMENT_ID_REQUIRED",
                  "message": "segment_id is required — choose which member segment to refresh. GET /v1/audiences/:id to list this audience's segments."
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "description": "The audience does not exist (simple-tier body, no `code`), or the given `segment_id` does not resolve to any segment document (`SEGMENT_NOT_FOUND`) despite having passed the membership check (e.g. deleted concurrently).\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "AudienceNotFound",
                      "required": [
                        "error"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "\"Audience not found\""
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "SegmentNotFound",
                      "required": [
                        "error",
                        "code",
                        "message"
                      ],
                      "properties": {
                        "error": {
                          "type": "string"
                        },
                        "code": {
                          "type": "string",
                          "enum": [
                            "SEGMENT_NOT_FOUND"
                          ]
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "409": {
            "description": "The named segment cannot be refreshed. `SEGMENT_ARCHIVED` — the segment is archived; restore it first (`PATCH` `status: active` via `PATCH /v1/segments/{id}`). `SHARED_SOURCE_DATA` — matched duplicate that references another segment's match data; refreshing it here would overwrite the source segment's upload. The message names the source segment id to refresh instead.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "SEGMENT_ARCHIVED",
                        "SHARED_SOURCE_DATA"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "422": {
            "description": "`MISSING_POSITIVE_CLASS_ID` — **(propensity only)** the segment predates `positive_class_segment_id` storage and cannot be auto-refreshed; delete and recreate it.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "MISSING_POSITIVE_CLASS_ID"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "500": {
            "description": "`WORKFLOW_LAUNCH_FAILED` — the similarity/propensity re-launch workflow call failed; retry. Presigned-URL generation failures for the matched subtype return a simple-tier body with no `code`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "type": "object",
                      "title": "WorkflowLaunchFailed",
                      "required": [
                        "error",
                        "code",
                        "message"
                      ],
                      "properties": {
                        "error": {
                          "type": "string"
                        },
                        "code": {
                          "type": "string",
                          "enum": [
                            "WORKFLOW_LAUNCH_FAILED"
                          ]
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    },
                    {
                      "type": "object",
                      "title": "PresignedUrlError",
                      "required": [
                        "error"
                      ],
                      "additionalProperties": false,
                      "properties": {
                        "error": {
                          "type": "string",
                          "description": "\"Failed to generate presigned upload URL\""
                        }
                      }
                    }
                  ]
                }
              }
            }
          }
        }
      }
    },
    "/v1/audiences/{id}/mappings": {
      "post": {
        "summary": "Confirm column mapping overrides for a matched audience",
        "operationId": "confirmAudienceMappings",
        "description": "Stores confirmed column mapping overrides for this audience's matched member segment. Call this after reviewing the `POST /v1/match/{id}/analyze` response to correct any incorrect predictions. Sets `mapping_confirmed: true` so the platform skips its own analysis and uses these mappings directly.\n\nIf the underlying segment was refreshed with `hitl: true`, the upload is still staged — this call also moves the staged file into place and starts identity resolution, which is why it can return 500 `STAGING_COPY_FAILED` if that move fails (the segment is left unchanged in that case; retry the call). Use `POST /v1/audiences/{id}/mappings/cancel` instead to abort a staged upload without confirming.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience document ID."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "column_mappings"
                ],
                "properties": {
                  "column_mappings": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    },
                    "description": "Map of source column name → standard identity attribute name. Use `\"(skip)\"` as the value to explicitly exclude a column.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Mappings saved.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "column_mappings"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    },
                    "column_mappings": {
                      "type": "object",
                      "additionalProperties": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "500": {
            "description": "STAGING_COPY_FAILED — moving a staged (`hitl: true`) upload into place failed; the segment is unchanged, retry the call.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "STAGING_COPY_FAILED"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/audiences/{id}/mappings/cancel": {
      "post": {
        "summary": "Abort a staged (hitl:true) matched-segment upload without confirming",
        "operationId": "cancelAudienceMappings",
        "description": "Audience-scoped counterpart of `POST /v1/segments/{id}/mappings/cancel` — resolves this audience's matched member segment and clears its staged upload. Performs no file move and no status/mapping-state change — the segment is left exactly as it was. Idempotent: returns 200 even if nothing is currently staged.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience document ID."
          }
        ],
        "responses": {
          "200": {
            "description": "Staged upload cleared (or already clear).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/deliveries/bulk": {
      "post": {
        "summary": "Bulk deliver multiple audiences",
        "operationId": "bulkDeliverAudiences",
        "description": "Accepts a list of audience IDs and shared delivery parameters. Fires N parallel delivery jobs and returns per-audience results as HTTP 207 Multi-Status. Partial failure is acceptable — failed items do not roll back succeeded ones. Download is the currently active destination; LiveRamp and Narrative return 503 before any item or billable record is created. Requires 'purchase' scope.\n",
        "tags": [
          "Deliveries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "audience_ids",
                  "destination"
                ],
                "properties": {
                  "audience_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 25,
                    "description": "IDs of audiences to deliver. Max 25 per request."
                  },
                  "destination": {
                    "type": "string",
                    "enum": [
                      "download",
                      "liveramp",
                      "narrative"
                    ],
                    "description": "Shared destination. `liveramp` and `narrative` currently return `DELIVERY_DESTINATION_UNAVAILABLE` without billing.\n"
                  },
                  "template_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Attribute template ID applied to every audience in the batch. Only `standard_iag` is valid. Mutually exclusive with field_list. If neither is provided, the Standard IAG attribute set is used.\n"
                  },
                  "field_list": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    },
                    "description": "Explicit attribute list applied to every audience in the batch — every attribute must be a valid audience attribute. Mutually exclusive with template_id. If neither is provided, the Standard IAG attribute set is used.\n"
                  },
                  "output_format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "avro",
                      "json",
                      "jsonl"
                    ],
                    "default": "csv",
                    "description": "Shared download artifact format for the batch."
                  },
                  "output_compression": {
                    "type": "string",
                    "enum": [
                      "none",
                      "gzip"
                    ],
                    "default": "none",
                    "description": "Shared artifact compression. gzip is supported for csv/json/jsonl; Avro uses native DEFLATE and rejects gzip.\n"
                  },
                  "session_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Chat session ID for push notification routing."
                  },
                  "campaign_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Campaign context for notification routing."
                  }
                }
              },
              "example": {
                "audience_ids": [
                  "aud_abc123",
                  "aud_def456"
                ],
                "destination": "download",
                "template_id": "standard_iag"
              }
            }
          }
        },
        "responses": {
          "207": {
            "description": "Multi-Status — per-audience results. Each entry contains `audience_id`, `delivery_id` (if created), `status`, and `error` (if failed). Billing capacity errors also expose the same machine-readable `code` and USD-denominated capacity details as the single-audience delivery endpoint.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "results"
                  ],
                  "properties": {
                    "results": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "audience_id",
                          "status"
                        ],
                        "properties": {
                          "audience_id": {
                            "type": "string"
                          },
                          "delivery_id": {
                            "type": "string",
                            "description": "Present when a delivery was created or found."
                          },
                          "status": {
                            "type": "string",
                            "enum": [
                              "processing",
                              "completed",
                              "error"
                            ]
                          },
                          "error": {
                            "type": "string",
                            "description": "Error message when status is 'error'."
                          },
                          "code": {
                            "type": "string",
                            "description": "Machine-readable billing code when billing prevents this item.",
                            "enum": [
                              "BILLING_INSUFFICIENT_BALANCE",
                              "BILLING_POSTPAY_CEILING_EXCEEDED",
                              "BILLING_CONSUMER_POSTPAY_CEILING_EXCEEDED",
                              "BILLING_PROJECTION_STALE",
                              "BILLING_NOT_READY"
                            ]
                          },
                          "required": {
                            "type": "number",
                            "description": "Conservative USD requirement for a prepay billing failure."
                          },
                          "available": {
                            "type": "number",
                            "description": "Effective USD availability for a prepay billing failure."
                          },
                          "shortfall": {
                            "type": "number",
                            "description": "USD shortfall for a prepay billing failure."
                          },
                          "accrued": {
                            "type": "number",
                            "description": "Projected USD spend for a postpay ceiling failure."
                          },
                          "ceiling": {
                            "type": "number",
                            "description": "Configured USD ceiling for a postpay ceiling failure."
                          },
                          "output_format": {
                            "type": "string",
                            "enum": [
                              "csv",
                              "avro",
                              "json",
                              "jsonl"
                            ],
                            "description": "Artifact format when an existing or new download delivery is returned."
                          },
                          "output_compression": {
                            "type": "string",
                            "enum": [
                              "none",
                              "gzip"
                            ],
                            "description": "Artifact compression when an existing or new download delivery is returned."
                          }
                        }
                      }
                    }
                  }
                },
                "example": {
                  "results": [
                    {
                      "audience_id": "aud_abc123",
                      "delivery_id": "dlv_xyz001",
                      "status": "processing"
                    },
                    {
                      "audience_id": "aud_def456",
                      "status": "error",
                      "error": "Audience expired on 2026-05-01. Run a refresh first."
                    }
                  ]
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/connections/{provider}": {
      "delete": {
        "summary": "Disconnect an outbound integration",
        "operationId": "deleteConnection",
        "description": "Disconnects the active HubSpot or Klaviyo account using an explicit retain-versus-remove policy for app-owned provider data. With `remove`, provider cleanup must succeed before the connection is revoked. With `retain`, existing provider data remains while future enrichment and monitoring stop. Requires 'account' scope and an owner/admin member.",
        "tags": [
          "Integrations"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "provider",
            "required": true,
            "description": "The outbound provider to disconnect.",
            "schema": {
              "type": "string",
              "enum": [
                "hubspot",
                "klaviyo"
              ]
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "app_data_policy"
                ],
                "properties": {
                  "app_data_policy": {
                    "type": "string",
                    "enum": [
                      "retain",
                      "remove"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The provider connection was disconnected.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": false,
                  "required": [
                    "disconnected"
                  ],
                  "properties": {
                    "disconnected": {
                      "type": "boolean",
                      "const": true
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/audiences/{id}/deliveries": {
      "get": {
        "summary": "List deliveries for an audience",
        "operationId": "listAudienceDeliveries",
        "description": "Returns all deliveries (past and pending) for the given audience, newest first. Requires 'discovery' scope.\n",
        "tags": [
          "Deliveries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience document ID.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "List of deliveries.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeliveryObject"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "summary": "Create a delivery for an audience",
        "operationId": "createAudienceDelivery",
        "description": "Initiates a delivery of the audience to the specified destination and emits usage only after successful egress. Download is currently active. LiveRamp and Narrative return 503 before a delivery or billable record is created until their partner pushes are implemented. Returns the new DeliveryObject — poll `GET /v1/audiences/{id}/deliveries/{delivery_id}` for status updates.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Deliveries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience document ID.\n"
          },
          {
            "name": "X-Integration-Run-Id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^ir_[A-Za-z0-9_-]{40}$"
            },
            "description": "OAuth partnership workflows only. Re-authorizes the private audience against the same provider grant and binds delivery, native billing, history, and promotion to the exact partnership run.\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "destination"
                ],
                "properties": {
                  "destination": {
                    "type": "string",
                    "enum": [
                      "download",
                      "liveramp",
                      "narrative"
                    ],
                    "description": "`download` is active. `liveramp` and `narrative` currently return `DELIVERY_DESTINATION_UNAVAILABLE` without billing.\n"
                  },
                  "template_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Enrichment template ID. Only `standard_iag` is valid (see `templates[]` in GET /v1/catalog/fields). Mutually exclusive with `field_list`. If neither `template_id` nor `field_list` is provided, the Standard IAG attribute set (all attributes with `product_usage` containing `audience`) is used.\n"
                  },
                  "field_list": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    },
                    "description": "Explicit list of attribute names to include in the export (alternative to `template_id`) — every attribute must be a valid audience attribute. Mutually exclusive with `template_id`. If neither is provided, the Standard IAG attribute set is used. For matched-subtype rows, individual-level attributes (e.g. `age`, `gender`) come back `null` for a row whose `match_level` doesn't qualify for individual-level data — this is expected, not an error. If `iag_household_id` is requested and available for a row, it is exported in the same org-scoped, non-reversible format as `iag_person_id`.\n"
                  },
                  "include_unmatched": {
                    "type": "boolean",
                    "default": true,
                    "description": "**(matched subtype only)** When `true` (default), the export includes rows from your upload that did not resolve to a real graph match — these appear with null enrichment attributes and, in the exported `iag_person_id` column, a best-effort id deterministically derived from the row's own identity signals (or `null` if none qualified) rather than a real platform match. Set to `false` to export resolved records only. Every exported row (matched or not) also carries a `row_id` column matching your original upload, so you can correlate a delivered row back to its source input row regardless of match status. Matched rows additionally carry `match_level` (`I`/`H`/`A`/`S`/`D`), `match_type` (e.g. `graph_name_email_match`, `vector_name_address_match`, `spatial_match`), and `match_confidence` (0–1) — all `null` (not derived) on unmatched rows.\n"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional HTTPS URL to receive `delivery.completed` or `delivery.failed` events for this delivery. Overrides the org-level webhook URL for this request only; if omitted, the org-configured URL is used as the fallback. Every dispatch is signed regardless of which URL is used — see the Webhooks tag for the envelope shape and signing scheme.\n"
                  },
                  "liveramp_seat_id": {
                    "type": "string",
                    "description": "(liveramp destination only) Your LiveRamp seat ID.\n"
                  },
                  "narrative_dataset_id": {
                    "type": "string",
                    "description": "(narrative destination only) Your Narrative.io dataset ID.\n"
                  },
                  "output_format": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "csv",
                      "avro",
                      "json",
                      "jsonl"
                    ],
                    "description": "Output file format for download deliveries. Omit to auto-detect: matched audiences default to the format of the original uploaded file (avro → avro, csv → csv, json → json, jsonl → jsonl); all other subtypes default to `csv`. Avro exports preserve native source column types including `REPEATED`/array attributes. Ignored for `liveramp` and `narrative` destinations.\n"
                  },
                  "output_compression": {
                    "type": "string",
                    "enum": [
                      "none",
                      "gzip"
                    ],
                    "default": "none",
                    "description": "Explicit artifact compression; it never inherits matched input compression. gzip is supported for csv/json/jsonl. Avro uses native DEFLATE and rejects outer gzip. Ignored for non-download destinations.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Delivery created and queued.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryObject"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/audiences/{id}/deliveries/{delivery_id}": {
      "get": {
        "summary": "Get a single delivery",
        "operationId": "getAudienceDelivery",
        "description": "Returns status, cost, download URL (if completed), and full metadata for a single delivery. Poll this endpoint after calling `POST /v1/audiences/{id}/deliveries` to track progress.\n\n**Note on `download_urls`**: for completed `download` deliveries, the server regenerates fresh 24-hour presigned URLs on every GET. This means repeated calls return different URLs — all pointing to the same file(s). Always use the `download_urls` from the most recent response.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Deliveries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Audience document ID.\n"
          },
          {
            "name": "delivery_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Delivery document ID.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Delivery details.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryObject"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/audiences/bulk": {
      "patch": {
        "summary": "Bulk update audiences",
        "operationId": "bulkPatchAudiences",
        "description": "Apply set_logic, segment add/remove, or status changes to multiple audiences in a single request. Audiences are subtype-agnostic composition wrappers, so this applies uniformly regardless of which segment subtypes (filter, matched, similarity, propensity) they compose. Fail-closed ownership validation — any unauthorized ID returns 403 before any writes. Max 50 audiences per call. Returns 207 for partial success.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "audience_ids"
                ],
                "properties": {
                  "audience_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 50,
                    "description": "IDs of audiences to update (1–50).\n"
                  },
                  "set_logic": {
                    "type": "string",
                    "enum": [
                      "union",
                      "intersection"
                    ],
                    "description": "New set_logic to apply to all specified audiences.\n"
                  },
                  "add_segment_id": {
                    "type": "string",
                    "description": "Segment ID to append to the included list of each audience.\n"
                  },
                  "remove_segment_id": {
                    "type": "string",
                    "description": "Segment ID to remove from the included list of each audience.\n"
                  },
                  "status": {
                    "type": "string",
                    "enum": [
                      "archived",
                      "active"
                    ],
                    "description": "Valid user-settable transitions, applied to every audience in `audience_ids`: `archived` — soft-deletes the audience; excluded from list results by default. `active` — restores a previously archived audience back to active; rejected with 400 (`INVALID_STATUS_TRANSITION`) if the current status is not `archived`. System-managed statuses (`pending`, `expired`, `failed`) cannot be set via this endpoint and are rejected with 400.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "All audiences updated.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "integer"
                    },
                    "failed": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "207": {
            "description": "Partial success — at least one audience failed. Check the `failed` array.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "updated": {
                      "type": "integer",
                      "description": "Number of audiences successfully updated."
                    },
                    "failed": {
                      "type": "array",
                      "description": "Details for each audience that could not be updated.",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "delete": {
        "summary": "Bulk archive (soft-delete) audiences",
        "operationId": "bulkArchiveAudiences",
        "description": "Soft-deletes multiple audiences in a single request. Fail-closed ownership validation. Max 50 per call.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "audience_ids"
                ],
                "properties": {
                  "audience_ids": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "minItems": 1,
                    "maxItems": 50
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Audiences archived.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "integer"
                    },
                    "not_found": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "description": "One or more IDs not owned by caller.\n"
          }
        }
      }
    },
    "/v1/audiences/bulk-create": {
      "post": {
        "summary": "Bulk create audiences",
        "operationId": "bulkCreateAudiences",
        "description": "Create up to 25 composition audiences in a single request. Each item wraps one or more pre-existing segments (any subtype — filter, matched, similarity, or propensity) via `segment_ids`/`excluded_segment_ids` or `segment_refs`; it does not create new segments. To generate a new matched/similarity/ propensity *segment* and its wrapper audience in one call, use `POST /v1/segments` with `create_audience: true` instead. Optionally links all created audiences to a campaign via the audience_links sub-collection.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Audiences"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "audiences"
                ],
                "properties": {
                  "audiences": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 25,
                    "items": {
                      "type": "object",
                      "required": [
                        "name"
                      ],
                      "properties": {
                        "name": {
                          "type": "string"
                        },
                        "segment_refs": {
                          "type": "array",
                          "description": "Explicit membership specs with role — preferred over segment_ids/excluded_segment_ids when present (takes precedence if both are supplied).\n",
                          "items": {
                            "type": "object",
                            "required": [
                              "segment_id",
                              "role"
                            ],
                            "properties": {
                              "segment_id": {
                                "type": "string"
                              },
                              "role": {
                                "type": "string",
                                "enum": [
                                  "include",
                                  "exclude"
                                ]
                              }
                            }
                          }
                        },
                        "segment_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "minItems": 1
                        },
                        "excluded_segment_ids": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          }
                        },
                        "set_logic": {
                          "type": "string",
                          "enum": [
                            "union",
                            "intersection"
                          ]
                        },
                        "record_count": {
                          "type": "integer"
                        },
                        "visibility": {
                          "type": "string",
                          "enum": [
                            "org",
                            "private"
                          ],
                          "description": "Visibility of the created audience. Defaults to 'org'.\n"
                        }
                      }
                    }
                  },
                  "campaign_id": {
                    "type": "string",
                    "description": "Link all created audiences to this campaign.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Audiences created.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "created": {
                      "type": "integer"
                    },
                    "audiences": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/AudienceObject"
                      }
                    },
                    "failed": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "error": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/segments/bulk-create": {
      "post": {
        "summary": "Bulk create segments",
        "operationId": "bulkCreateSegments",
        "description": "Create up to 25 filter-subtype segments in one request. Each segment is created independently (own `versions/1` snapshot); failures are reported per item in `errors` and do not roll back successful items. Returns 201 when at least one segment was created, 400 (same body shape) when every item failed. Use `POST /v1/segments` individually for matched/similarity/propensity segments.\n      Requires 'purchase' scope.\n\nEach item's `filters`/`filter_groups` are validated against the live Field Catalog before it's created — an unknown field, a disallowed operator, or an invalid enum value fails that item only, reported in `errors` (same as any other per-item failure). If the Field Catalog itself is temporarily unreachable, the entire request fails with 502 `FILTER_CATALOG_UNAVAILABLE` rather than creating some segments unvalidated.\n",
        "tags": [
          "Segments"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "segments"
                ],
                "properties": {
                  "segments": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 25,
                    "items": {
                      "type": "object",
                      "required": [
                        "name"
                      ],
                      "properties": {
                        "name": {
                          "type": "string",
                          "minLength": 1,
                          "maxLength": 120,
                          "description": "Display name for the segment."
                        },
                        "subtype": {
                          "type": "string",
                          "enum": [
                            "filter",
                            "matched",
                            "similarity",
                            "propensity"
                          ],
                          "default": "filter",
                          "description": "Only 'filter' is supported by bulk-create — any other value fails per-item with an entry in `errors`.\n"
                        },
                        "filters": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/Filter"
                          },
                          "description": "Legacy flat filter list."
                        },
                        "filter_groups": {
                          "type": "array",
                          "items": {
                            "$ref": "#/components/schemas/FilterGroup"
                          },
                          "description": "Filter groups defining the segment criteria."
                        },
                        "count_snapshot": {
                          "type": "number",
                          "minimum": 0,
                          "description": "Optional known record count to seed the segment with."
                        },
                        "create_audience": {
                          "type": "boolean",
                          "default": false,
                          "description": "When true, auto-creates a thin audience wrapper for this segment (returned as `audience_id` on the created segment).\n"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "At least one segment created. Check `errors` for partial failures.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkCreateSegmentsResult"
                }
              }
            }
          },
          "400": {
            "description": "Every input item failed (`created: 0`) — same body shape as 201 with per-item reasons in `errors` — or the request body failed validation (standard error shape).\n",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/BulkCreateSegmentsResult"
                    },
                    {
                      "$ref": "#/components/schemas/ErrorResponse"
                    }
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "502": {
            "description": "Field Catalog temporarily unreachable — no items were validated or created (code FILTER_CATALOG_UNAVAILABLE)."
          }
        }
      }
    },
    "/v1/catalog/fields": {
      "get": {
        "summary": "Attribute catalog with pricing",
        "operationId": "getCatalogFields",
        "description": "Returns the list of available enrichment attributes — every attribute with `product_usage` containing `audience` (the definition of a valid user-facing IAG attribute) — with coverage rates and per-attribute pricing, plus the single system delivery template. Use this to browse available attributes before creating a delivery.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Enrichment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Attribute catalog.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "fields": {
                      "type": "array",
                      "description": "Available enrichment attributes from the column registry.\n",
                      "items": {
                        "type": "object",
                        "properties": {
                          "field_name": {
                            "type": "string",
                            "description": "Programmatic attribute name to use in `field_list`.\n"
                          },
                          "label": {
                            "type": "string",
                            "description": "Human-readable display label.\n"
                          },
                          "data_type": {
                            "type": "string",
                            "description": "Source data type (e.g. STRING, INT64, FLOAT64).\n"
                          },
                          "coverage_rate": {
                            "type": "number",
                            "description": "Fraction of records that have a value for this attribute (0.0–1.0).\n"
                          }
                        }
                      }
                    },
                    "templates": {
                      "type": "array",
                      "description": "Always exactly one entry — the Standard IAG template, usable as a `template_id` shortcut. Its `fields` is the full audience attribute set above, resolved live from the registry (not a hardcoded list).\n",
                      "items": {
                        "type": "object",
                        "properties": {
                          "template_id": {
                            "type": "string",
                            "enum": [
                              "standard_iag"
                            ],
                            "description": "ID to pass as `template_id` in delivery or quote requests.\n"
                          },
                          "label": {
                            "type": "string",
                            "description": "Human-readable template name.\n"
                          },
                          "fields": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Attribute names included in this template.\n"
                          }
                        }
                      },
                      "example": [
                        {
                          "template_id": "standard_iag",
                          "label": "Standard IAG",
                          "fields": [
                            "age",
                            "gender",
                            "estimated_household_income"
                          ]
                        }
                      ]
                    },
                    "pricing": {
                      "type": "object",
                      "description": "Effective per-record pricing for the calling org.\n",
                      "properties": {
                        "base_record_price": {
                          "type": "number",
                          "description": "Base cost per record (USD).\n"
                        },
                        "matched_record_price": {
                          "type": "number",
                          "description": "Cost per resolved record for matched audiences (USD).\n"
                        },
                        "micro_batch_price_per_record": {
                          "type": "number",
                          "description": "Cost per resolved record for synchronous micro-batch matching (USD)."
                        },
                        "field_surcharges": {
                          "type": "object",
                          "description": "Additional per-record cost for specific attributes (field_name → USD).\n",
                          "additionalProperties": {
                            "type": "number"
                          }
                        },
                        "delivery_surcharges": {
                          "type": "object",
                          "description": "Per-record surcharge by destination (destination → USD).\n",
                          "additionalProperties": {
                            "type": "number"
                          }
                        },
                        "usage_discount_bps": {
                          "type": "integer",
                          "minimum": 0,
                          "maximum": 10000,
                          "description": "Contract usage-rate discount in basis points; commitment charges and minimums are unchanged."
                        },
                        "projected_at": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "date-time"
                        }
                      },
                      "required": [
                        "base_record_price",
                        "matched_record_price",
                        "micro_batch_price_per_record",
                        "field_surcharges",
                        "delivery_surcharges",
                        "usage_discount_bps",
                        "projected_at"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/data-release": {
      "get": {
        "summary": "Current data release version",
        "operationId": "getDataRelease",
        "description": "Returns the platform's current data-release version — the same value `POST /v1/match`'s per-row `resolution_action: \"current\"` label compares a previously-delivered identity's stored refresh-major against. Cacheable: backed by a cache refreshed roughly every 15 minutes, not a live query.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Enrichment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current data-release version.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "current_release_major": {
                      "type": "integer",
                      "description": "Current full-warehouse-rebuild release version. A previously-delivered identity's stored refresh-major equal to this value is guaranteed unchanged since last delivered.\n"
                    },
                    "current_release_minor": {
                      "type": "integer",
                      "description": "Current incremental-delta release version within the major release."
                    },
                    "as_of": {
                      "type": "string",
                      "format": "date-time",
                      "description": "When this snapshot was last refreshed."
                    }
                  },
                  "required": [
                    "current_release_major",
                    "current_release_minor",
                    "as_of"
                  ]
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "503": {
            "description": "The data-release registry has not been populated yet. Retry shortly.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          }
        }
      }
    },
    "/v1/match": {
      "post": {
        "summary": "Micro-batch synchronous match",
        "operationId": "matchMicroBatch",
        "description": "Synchronous inline matching for small record sets (up to 100 records). Submit your records and the list of attributes you want appended, and receive matched results immediately.\n\n**Person IDs:** A real match returns a tier `01` `iag_person_id`: a stable, non-reversible ID salted for your organization. The same real identity is consistent inside your organization and different in every other organization. An unmatched row may receive a derived tier `02`–`05` ID from its own submitted signals. That derived value is a continuity label—not a graph match, licensed identity, refresh key, or resolved audience member. Use the match metadata and `match_count`, not ID presence alone, to identify real matches.\n\n**Billing:** Usage is emitted per matched record only (unmatched records are free). The platform reserves a conservative ceiling and durably queues finalized usage for billing rather than debiting a local balance field. `amount_charged` is the conservative USD estimate accepted for provider delivery; it is not a finalized invoice amount.\n\n**Records array:** one output record per input record — matched or not. Unmatched rows carry `match_level`/`match_type`/`match_confidence: null` and a best-effort `iag_person_id` derived from the row's own identity signals (or `null` if none qualified) — only real matches are billed. Use `match_count` vs `record_count` to distinguish real matches from derived-only rows.\n\nFor datasets larger than 100 records, use `POST /v1/match/file` instead. Upload, analysis, and async matching are free until egress. The first successful delivery involving that file-match run emits its one aggregate match usage set together with normal delivery usage.\n\n**Retries:** pass an `Idempotency-Key` header to make a retried call safe to repeat without a duplicate charge. The same key with an identical body returns the original response verbatim, at zero additional charge (honored for 24 hours); the same key with a different body is rejected with 409 `IDEMPOTENCY_KEY_CONFLICT`. Without this header, every call reserves and charges independently — recommended for any client that may retry on timeout.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Enrichment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{7,159}$"
            },
            "description": "Optional. Makes a retried call safe to repeat — see the endpoint description's **Retries** section.\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "records"
                ],
                "properties": {
                  "records": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 100,
                    "description": "Records to match (max 100). Each object should include at least one identity signal: `email` (or `email_sha256`), `phone` (or `phone_sha256`), `full_name` (or `first_name`+`last_name`), or address attributes (`address_1`, `city`, `state`, `zip`). All attributes are optional — include only what you have. Any extra attributes you include are echoed back unchanged.\n",
                    "items": {
                      "type": "object",
                      "additionalProperties": true,
                      "properties": {
                        "email": {
                          "type": "string",
                          "format": "email",
                          "description": "Primary email address."
                        },
                        "phone": {
                          "type": "string",
                          "description": "Phone number in any standard format."
                        },
                        "email_sha256": {
                          "type": "string",
                          "description": "Lowercase-hex SHA-256 hash of a normalized email address, for clients whose own systems never expose raw email addresses (e.g. data co-op / clean-room partners using the same hashing convention). The platform passes this through without re-hashing. Do not submit an already-hashed value in `email` — it would be hashed a second time and never match.\n"
                        },
                        "phone_sha256": {
                          "type": "string",
                          "description": "Lowercase-hex SHA-256 hash of a normalized phone number. Same pre-hashed semantics as `email_sha256`.\n"
                        },
                        "full_name": {
                          "type": "string",
                          "description": "Optional convenience field. When provided (and first_name / last_name are absent), the engine parses this into first_name, middle_name, last_name, and name_suffix automatically. The parsed components are used for matching; the original full_name value is echoed back unchanged.\n"
                        },
                        "first_name": {
                          "type": "string"
                        },
                        "middle_name": {
                          "type": "string"
                        },
                        "last_name": {
                          "type": "string"
                        },
                        "name_suffix": {
                          "type": "string",
                          "description": "Generational suffix (e.g. `JR`, `SR`, `III`)."
                        },
                        "emails": {
                          "type": "array",
                          "items": {
                            "type": "string",
                            "format": "email"
                          },
                          "description": "Additional raw email addresses beyond `email`, tried as extra match permutations.\n"
                        },
                        "emails_sha256": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Pre-hashed counterpart to `emails`."
                        },
                        "phones": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Additional raw phone numbers beyond `phone`, tried as extra match permutations.\n"
                        },
                        "phones_sha256": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Pre-hashed counterpart to `phones`."
                        },
                        "addresses": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "address_1": {
                                "type": "string"
                              },
                              "address_2": {
                                "type": "string"
                              },
                              "city": {
                                "type": "string"
                              },
                              "state": {
                                "type": "string"
                              },
                              "zip": {
                                "type": "string"
                              }
                            }
                          },
                          "description": "Additional addresses beyond `address_1`/`city`/`state`/`zip`, tried as extra match permutations.\n"
                        },
                        "address_1": {
                          "type": "string",
                          "description": "Street address line 1."
                        },
                        "address_2": {
                          "type": "string",
                          "description": "Street address line 2 (apt, suite, etc.)."
                        },
                        "city": {
                          "type": "string"
                        },
                        "state": {
                          "type": "string",
                          "description": "Two-letter US state code."
                        },
                        "zip": {
                          "type": "string",
                          "description": "5- or 9-digit ZIP code."
                        },
                        "dob": {
                          "type": "string",
                          "description": "Date of birth — any parseable date format (e.g. YYYY-MM-DD)."
                        },
                        "iag_person_id": {
                          "type": "string",
                          "description": "A previously-issued tier `01` person ID from this organization (format `CLIENTCODE_TIER_HASH`, e.g. returned by a prior call to this endpoint or a matched-segment delivery). A valid ID can go directly to refresh without repeating identity matching. A foreign, malformed, or derived tier `02`–`05` ID cannot be used for direct refresh; the row falls back to its other identity signals, if any.\n"
                        }
                      }
                    }
                  },
                  "field_list": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    },
                    "description": "Enrichment attribute names to append on match — every attribute must be a valid audience attribute (see `GET /v1/catalog/fields`). Mutually exclusive with `template_id`. If neither is provided, the Standard IAG attribute set (all attributes with `product_usage` containing `audience`) is used. Individual-level attributes (e.g. `age`, `gender`) come back `null` per row when that row's `match_level` doesn't qualify for individual-level data (see `match_level` below) — this is expected, not an error. Request only the attributes valid for the `match_level`(s) you accept if you want to avoid nulls.\n"
                  },
                  "template_id": {
                    "type": "string",
                    "description": "Pre-built enrichment bundle ID. Only `standard_iag` is valid (see `templates[]` in `GET /v1/catalog/fields`). Mutually exclusive with `field_list`. If neither is provided, the Standard IAG attribute set is used.\n"
                  },
                  "match_level": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "I",
                        "H",
                        "D",
                        "S",
                        "A"
                      ]
                    },
                    "description": "Filter results to specific match levels. `I` = individual, `H` = household, `D` = digital (email/phone only match), `S` = spatial (nearby-address proximity match), `A` = address-level. Defaults to all 5 levels (`['I', 'H', 'D', 'S', 'A']`) if omitted. Restricting this also restricts which attributes come back populated — see `field_list` above.\n"
                  },
                  "create_scratch_segment": {
                    "type": "boolean",
                    "default": false,
                    "description": "When true, materialize the real matched identities from this completed response as a reusable 24-hour matched scratch segment. This reuses the already-paid result and does not run matching or billing again. The response's `scratch_segment` is null when no identities matched.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Matched records.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "records": {
                      "type": "array",
                      "description": "One output record per input record — matched or not. Matched rows carry a resolved `iag_person_id`, `match_level`/`match_type`/`match_confidence`, and (when requested) enrichment attributes. Unmatched rows carry `match_level`/`match_type`/`match_confidence: null` and a best-effort derived `iag_person_id` (see `iag_person_id` below) computed from the row's own identity signals, or `null` if none qualified. Use `match_count` vs `record_count` to distinguish real matches from derived-only rows — only real matches are billed. Input attributes are echoed back unchanged; resolved enrichment attributes are added alongside them.\n",
                      "items": {
                        "type": "object",
                        "additionalProperties": true,
                        "properties": {
                          "row_id": {
                            "type": "string",
                            "description": "Correlates a matched output row back to its position in the submitted records array (1-indexed as a string when the input didn't supply one).\n"
                          },
                          "email": {
                            "type": "string",
                            "format": "email"
                          },
                          "phone": {
                            "type": "string"
                          },
                          "email_sha256": {
                            "type": "string",
                            "description": "Present when the request included `email_sha256`, echoed back unchanged.\n"
                          },
                          "phone_sha256": {
                            "type": "string",
                            "description": "Present when the request included `phone_sha256`, echoed back unchanged.\n"
                          },
                          "full_name": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Present when the request included full_name; the original submitted value, echoed back unchanged. Null when the request did not include one.\n"
                          },
                          "first_name": {
                            "type": "string"
                          },
                          "middle_name": {
                            "type": "string",
                            "description": "Echoed back when present on input, or populated from `full_name` parsing when the request supplied `full_name` without `first_name`/`last_name`.\n"
                          },
                          "last_name": {
                            "type": "string"
                          },
                          "name_suffix": {
                            "type": "string",
                            "description": "Generational suffix (e.g. `JR`, `SR`, `III`). Echoed back when present on input, or populated from `full_name`/`last_name` parsing.\n"
                          },
                          "address_1": {
                            "type": "string"
                          },
                          "address_2": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          },
                          "zip": {
                            "type": "string"
                          },
                          "dob": {
                            "type": "string"
                          },
                          "emails": {
                            "type": "array",
                            "items": {
                              "type": "string",
                              "format": "email"
                            },
                            "description": "Additional raw emails from the request, echoed back unchanged."
                          },
                          "emails_sha256": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Additional pre-hashed emails from the request, echoed back unchanged."
                          },
                          "phones": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Additional raw phones from the request, echoed back unchanged."
                          },
                          "phones_sha256": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            },
                            "description": "Additional pre-hashed phones from the request, echoed back unchanged."
                          },
                          "addresses": {
                            "type": "array",
                            "items": {
                              "type": "object",
                              "additionalProperties": true
                            },
                            "description": "Additional addresses from the request, echoed back unchanged."
                          },
                          "iag_person_id": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "Org-scoped, non-reversible identifier — never a raw internal id. The same real identity receives a consistent tier-01 ID within one organization and a different ID in every other organization. Format: `CLIENTCODE_TIER_HASH`. `TIER` is `01` for a real graph match (see `match_level`/`match_type`/`match_confidence`) or `02`–`05` for a best-effort id deterministically derived from the row's own identity signals when it did NOT match (name+address, email, name+phone, or name+zip, in that priority order). `null` when the row is unmatched and no identity signal qualified for derivation. A tier-01 id may be resubmitted as input (see `iag_person_id` on the request schema above) to re-resolve the same match; derived (tier 02–05) ids cannot be.\n"
                          },
                          "match_level": {
                            "type": "string",
                            "enum": [
                              "I",
                              "H",
                              "A",
                              "S",
                              "D"
                            ],
                            "description": "Granularity of the match. `I` = individual, `H` = household, `A` = address-level, `S` = spatial (nearby-address proximity match), `D` = digital (email/phone only match). Present only on matched records (`null`, not a derived value, on unmatched rows).\n"
                          },
                          "match_type": {
                            "type": "string",
                            "description": "Matching strategy that produced the result (e.g. `graph_name_email_match`, `vector_name_address_match`, `spatial_match`). Present only on matched records.\n"
                          },
                          "match_confidence": {
                            "type": "number",
                            "description": "Confidence score for the match (0–1). Present only on matched records.\n"
                          },
                          "enrichment": {
                            "type": "object",
                            "additionalProperties": true,
                            "description": "Requested enrichment attributes keyed by column name (e.g. `acs_housing_units`, `usda_median_hh_income_2023`). Only present when `field_list` or `template_id` was provided and the record matched.\n"
                          },
                          "resolution_action": {
                            "type": "string",
                            "enum": [
                              "resolved",
                              "refreshed",
                              "current",
                              "unmatched",
                              "identity_unavailable"
                            ],
                            "description": "Customer-facing resolution outcome for this row, orthogonal to `license_action` below. `resolved` = an organic PII match (no id submitted for this row). `refreshed` = the caller submitted a valid `iag_person_id` for this row (direct Refresh) and the platform cannot confirm this identity's enrichment is unchanged since it was last delivered — whether or not this call was also billed (see `license_action`). `current` = same as `refreshed`, but the platform's data-release registry (see `GET /v1/data-release`) confirms that the identity remains on the same refresh-major release as its last delivery. The response contains current-mart values for the fields requested in this call; the requested field set may differ from an earlier call. `unmatched` = no real match, never licensed. `identity_unavailable` = a previously valid seeded id no longer resolves to deliverable data (deletion, suppression, retirement, or a split graph identity — the underlying reason is deliberately never distinguished here); the record otherwise looks exactly like an ordinary unmatched row. The platform's full vocabulary also defines `invalid`, reserved for a reject-the-row policy this API does not yet implement — it is not returned today, but a client should not treat its future appearance as a breaking change.\n"
                          },
                          "license_action": {
                            "type": "string",
                            "enum": [
                              "none",
                              "started",
                              "renewed"
                            ],
                            "description": "License effect of this row's resolution, independent of `resolution_action` — a row can simultaneously be `resolution_action: resolved` and `license_action: renewed`. `none` = no license period was started or renewed this call (already active, unmatched, or identity_unavailable). `started` = this identity had no prior confirmed license claim and one was started. `renewed` = a prior confirmed claim existed (even if expired) and the period was renewed.\n"
                          },
                          "license_expires_at": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "description": "ISO-8601 timestamp the identity's current license period expires. Populated when reusing an existing active license (`license_action: none`) and for a row starting or renewing a period in this same call (`license_action: started` or `renewed` — a fixed 12 months from now). Null when no license period applies (e.g. an unmatched or identity_unavailable row).\n"
                          }
                        }
                      }
                    },
                    "record_count": {
                      "type": "integer",
                      "description": "Total number of input records processed.\n"
                    },
                    "match_count": {
                      "type": "integer",
                      "description": "Number of records that resolved to a known identity.\n"
                    },
                    "billable_identity_count": {
                      "type": "integer",
                      "description": "Number of unique real identities charged for this operation, deduplicated by internal identity rather than row count. Duplicate rows resolving to the same identity are counted once; a resubmitted `iag_person_id` for an identity with an active license contributes zero (free Refresh). May be less than or equal to `match_count`, and is the value actually billed — `match_count` describes matching, not billing.\n"
                    },
                    "match_rate": {
                      "type": "number",
                      "description": "match_count / record_count.\n"
                    },
                    "amount_charged": {
                      "type": "number",
                      "description": "Conservative platform estimate in USD for usage accepted for delivery (e.g. `0.30` means $0.30 estimated). This legacy-named field is not a local credit debit or finalized invoice amount; unmatched records emit no usage.\n"
                    },
                    "run_id": {
                      "type": "string",
                      "description": "Unique identifier for this match run. Pass to `GET /v1/match/runs/{runId}` to retrieve full details or audit this call later. Requires 'discovery' scope to read.\n"
                    },
                    "scratch_segment": {
                      "oneOf": [
                        {
                          "type": "object",
                          "additionalProperties": false,
                          "required": [
                            "segment_id",
                            "name",
                            "record_count",
                            "ephemeral",
                            "expires_at"
                          ],
                          "properties": {
                            "segment_id": {
                              "type": "string"
                            },
                            "name": {
                              "type": "string"
                            },
                            "record_count": {
                              "type": "integer",
                              "minimum": 1
                            },
                            "ephemeral": {
                              "type": "boolean",
                              "const": true
                            },
                            "expires_at": {
                              "type": "string",
                              "format": "date-time"
                            }
                          }
                        },
                        {
                          "type": "null"
                        }
                      ],
                      "description": "The reusable matched scratch segment created from this call when `create_scratch_segment` is true; null when there were no real matches.\n"
                    },
                    "scratch_segment_error": {
                      "type": "object",
                      "additionalProperties": false,
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "string",
                          "const": "SCRATCH_SEGMENT_CREATION_FAILED"
                        },
                        "message": {
                          "type": "string"
                        }
                      },
                      "description": "Present only when matching and billing succeeded but the optional, non-billable scratch-segment materialization failed.\n"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "description": "BILLING_NOT_READY — the billing account/route isn't active. `IDEMPOTENCY_KEY_CONFLICT` — the `Idempotency-Key` header was already used with a different request body. `IDEMPOTENCY_REQUEST_IN_PROGRESS` — another request with this key is in flight; retry after the seconds in `Retry-After`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "BILLING_NOT_READY",
                        "IDEMPOTENCY_KEY_CONFLICT",
                        "IDEMPOTENCY_REQUEST_IN_PROGRESS"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/integration-runs": {
      "get": {
        "summary": "List retained partnership activity",
        "operationId": "listIntegrationRuns",
        "description": "Returns safe aggregate summaries for batch/file partnership workflows. Raw workflow objects, identity manifests, row data, and signed URLs are never returned. Use at most one optional filter. Requires 'discovery' scope.\n",
        "tags": [
          "Partnerships"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "connection_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "kind",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "batch_enrichment",
                "file_enrichment",
                "ingest",
                "activation",
                "export"
              ]
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "created",
                "processing",
                "awaiting_boundary",
                "settling",
                "completed",
                "failed",
                "cancelled"
              ]
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 25
            }
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Retained partnership activity",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationRunListResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/integration-runs/{runId}": {
      "get": {
        "summary": "Get retained partnership activity",
        "operationId": "getIntegrationRun",
        "description": "Returns one safe partnership-run projection without private artifact or manifest references. Requires 'discovery' scope.\n",
        "tags": [
          "Partnerships"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "runId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^ir_[A-Za-z0-9_-]{40}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Partnership activity detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationRun"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/integration-match-collections": {
      "get": {
        "summary": "List saved integration match collections",
        "operationId": "listIntegrationMatchCollections",
        "description": "Lists private provider-neutral holding collections containing already-settled integration matches. Requires 'discovery' scope.",
        "tags": [
          "Partnerships"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Saved integration match collections",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "collections"
                  ],
                  "properties": {
                    "collections": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/IntegrationMatchCollection"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/integration-match-collections/{collectionId}/materializations/preview": {
      "post": {
        "summary": "Preview a saved-match segment materialization",
        "operationId": "previewIntegrationMatchMaterialization",
        "description": "Returns exact deduplication, privacy, overlap, and net-addition counts without mutation or usage. Requires 'discovery' scope.",
        "tags": [
          "Partnerships"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "collectionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationMaterializationPreviewRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Exact materialization preview",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationMaterializationPreviewResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/integration-match-collections/{collectionId}/materializations": {
      "post": {
        "summary": "Build or extend a segment from saved integration matches",
        "operationId": "createIntegrationMatchMaterialization",
        "description": "Idempotently creates a matched segment or extends a compatible standalone matched segment without rerunning matching, creating an audience, or recording usage. Requires 'purchase' scope.",
        "tags": [
          "Partnerships"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "collectionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 240
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IntegrationMaterializationRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Existing idempotent materialization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationMaterialization"
                }
              }
            }
          },
          "201": {
            "description": "Materialization completed and the segment is available",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationMaterialization"
                }
              }
            }
          },
          "202": {
            "description": "Materialization intent is durable and queued for processing",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationMaterialization"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/integration-match-materializations/{materializationId}": {
      "get": {
        "summary": "Get a saved-match segment materialization",
        "operationId": "getIntegrationMatchMaterialization",
        "description": "Returns safe aggregate materialization status. Requires 'discovery' scope.",
        "tags": [
          "Partnerships"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "materializationId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^imm_[a-f0-9]{40}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Saved-match materialization",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IntegrationMaterialization"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/match/file": {
      "post": {
        "summary": "Start an async file-based identity resolution job",
        "operationId": "createFileMatch",
        "description": "Creates a **matched segment** by uploading a customer list for identity resolution. Equivalent to `POST /v1/segments` with `subtype: matched`.\n\nUpload csv, newline-delimited json/jsonl, or Avro records to the Infinite Audience identity graph and receive an enriched file back. Each format may be raw or gzip. Useful for CRM onboarding, suppression list resolution, contact data hygiene, and data co-op workflows.\n\n**File structure:** there is no fixed column schema — column names are inferred via AI (`POST /v1/match/{segment_id}/analyze` reports the mapping, a confidence score per column, and any warnings). For the best match quality, name your columns (or use names that closely resemble) the standard identity attributes: `email`, `phone`, `full_name` (or separately, `first_name`, `middle_name`, `last_name`, `name_suffix`), `address_1`, `address_2`, `city`, `state`, `zip`, `dob`, `iag_person_id` (a re-upload of an id previously returned by this platform — format `CLIENTCODE_TIER_HASH` — resolved server-side before matching). Only map a column to `full_name` when the file has a single combined-name column — if separate first/last name columns exist, map those instead. Only a valid tier `01` ID previously issued to this organization can go directly to refresh. Derived tier `02`–`05` IDs are continuity labels for unmatched rows, not resolved identities; submit the row's identity signals again if you want to attempt resolution. A column that can't be confidently mapped is excluded from identity resolution — if a critical attribute (`email`, `phone`, `last_name`) ends up unmapped, match accuracy drops or the segment may fail to resolve at all.\n\n**Pre-hashed identity columns:** for clients whose own systems never expose raw email/phone values, map a column to `email_sha256` or `phone_sha256` (lowercase-hex SHA-256 digests) instead of the raw `email`/`phone` target — the platform passes these through without re-hashing. Do not map an already-hashed column to the raw `email`/`phone` target (it would be hashed a second time and never match), and do not map a raw email/phone column to `email_sha256`/`phone_sha256` just because of its name — the mapper trusts sample-value shape over column name. `avro`/`json`/ `jsonl` uploads (which support array columns) may also supply `emails`, `phones`, `addresses`, `emails_sha256`, and `phones_sha256` as extra match permutations beyond the primary identity fields. If your upload includes `row_id`, every value must be unique across all shards. When it is absent, the platform assigns stable `row_0`, `row_1`, ... values. Duplicate ids or collisions with generated values fail the match before its result can be delivered.\n\n**Billing:** Upload, analysis, and matching completion emit no billable usage. The first successful `POST /v1/audiences/{id}/deliveries` or `POST /v1/match/{id}/deliveries` involving the current run emits its one aggregate `platform_match` usage set together with audience, destination, and enrichment usage. Retries, duplicates, and later deliveries do not emit that file-match usage again.\n\n**Workflow (hitl unset/false — default, upload resolves automatically):**\n1. **POST /v1/match/file** — receive a `segment_id` and a 30-minute presigned\n   `upload_url`. Pass `file_format` to specify your file type (default `csv`).\n   When `create_audience: true` (default), also receives an `audience_id`.\n\n2. **Upload your file** — `PUT` to `upload_url` with the underlying\n   format's Content-Type. For gzip, send raw gzip bytes and do not set\n   Content-Encoding. No `Authorization` header is needed.\n\n3. *(Optional)* **`POST /v1/match/{segment_id}/analyze`** — preview column\n   mapping. Note that by the time this can return real data, the upload has\n   already triggered automatic resolution — this step does not pause anything.\n\n4. **Automatic column analysis** — the platform always runs column analysis as part\n   of the match workflow. If no usable identity attributes can be resolved, the\n   segment transitions to `failed` and `error_message` describes the issue.\n\n   **Standard column names for best results:** `email`, `phone`, `full_name`,\n   `first_name`, `middle_name`, `last_name`, `name_suffix`, `address_1`,\n   `address_2`, `city`, `state`, `zip`, `dob`, `iag_person_id`. Pre-hashed\n   `email_sha256`/`phone_sha256` are also supported — see below.\n\n5. Poll **`GET /v1/match/file/{match_id}`** until `matching_status: completed` (or use a\n   `segment.ready` webhook).\n\n6. **`POST /v1/match/{match_id}/deliveries`** — export the already resolved result.\n   Poll **`GET /v1/match/{match_id}/deliveries`** for status; it takes the match id\n   directly and needs no audience id — if you only want matching/enrichment and\n   don't need audience-building concepts, this and step 1's `segment_id`/`match_id`\n   are the only ids you need to track end to end. (The audience-scoped\n   `POST /v1/audiences/{audience_id}/deliveries` also works and is what this endpoint\n   forwards to internally, but requires the audience id from step 1.)\n\n\n**Workflow (`hitl: true` — requires an explicit confirm before resolution runs):**\n1. **POST /v1/match/file** `{\"hitl\": true}` — receive a `segment_id` and `upload_url`.\n2. **Upload your file** — lands at a staging location, NOT the path that\n   triggers automatic resolution.\n\n3. **`POST /v1/match/{segment_id}/analyze`** — reads the staged file; safe to\n   call any time after upload, since nothing has started yet.\n\n4. **`POST /v1/segments/{segment_id}/mappings`** to confirm (moves the file\n   into place and starts resolution) or\n   **`POST /v1/segments/{segment_id}/mappings/cancel`** to abort (nothing\n   ever ran).\n\n5. Poll **`GET /v1/match/file/{match_id}`** until `matching_status: completed`, then\n   **`POST /v1/match/{match_id}/deliveries`** — same match-namespaced export and\n   polling as the default flow above (see step 6 there).\n\n\n**Sharded uploads:** set `shard_count` > 1 for same-format files. The response returns index-ordered `upload_urls`; upload every declared shard. Every CSV shard must include the same header row, and all shards must share schema and compression. Analysis samples shard 0; a later mismatch fails the job explicitly.\n\n**Retries:** pass an `Idempotency-Key` header to make a retried create call safe to repeat: the same key with an identical body returns the original segment/audience (and freshly re-signed upload URL(s), if the originals already expired) instead of creating a duplicate. Unlike `POST /v1/match`, this endpoint isn't billed at creation, so a duplicate without this header is an orphan Library entry, not a double charge — recommended for any client that may retry.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Enrichment"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 160,
              "pattern": "^[A-Za-z0-9][A-Za-z0-9._:-]{7,159}$"
            },
            "description": "Optional. Makes a retried create call safe to repeat — see the endpoint description's **Retries** section.\n"
          },
          {
            "name": "X-Integration-Execution-Key",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string",
              "minLength": 8,
              "maxLength": 220,
              "pattern": "^[A-Za-z0-9:_-]{8,220}$"
            },
            "description": "OAuth partnerships only. A provider-stable execution key that creates or replays a private partnership run. Use the returned integration_run_id for status and delivery requests.\n"
          }
        ],
        "requestBody": {
          "required": false,
          "description": "All attributes are optional. You may omit the request body entirely or send `{}` — both are equivalent and will create a CSV match job with an auto-generated name.\n",
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Optional label for this match job. Defaults to `\"File Match — {ISO date}\"` if omitted.\n"
                  },
                  "file_format": {
                    "type": "string",
                    "enum": [
                      "csv",
                      "avro",
                      "json",
                      "jsonl"
                    ],
                    "default": "csv",
                    "description": "Input file format. `csv` (default) treats every column as a single scalar value — multi-value/array attributes (email, phone, address_1, etc.) are not supported in csv. `avro` supports native array columns — use it when your file already encodes multi-value attributes as proper arrays. `json` and `jsonl` also support native array columns and are parsed identically — newline-delimited JSON, one record object per line (not a single top-level JSON array).\n"
                  },
                  "compression": {
                    "type": "string",
                    "enum": [
                      "none",
                      "gzip"
                    ],
                    "default": "none",
                    "description": "gzip means the complete underlying file is a gzip stream. Upload raw gzip bytes with the underlying format's Content-Type and no Content-Encoding. gzip is not supported with file_format=avro (avro is already internally compressed) — rejected with 400 UNSUPPORTED_INPUT_COMPRESSION.\n"
                  },
                  "create_audience": {
                    "type": "boolean",
                    "default": true,
                    "description": "When true (default), automatically creates a thin audience wrapper referencing the new matched segment. The `audience_id` is returned in the response and can be used for delivery immediately once the segment is active.\n"
                  },
                  "campaign_id": {
                    "type": "string",
                    "description": "Optional. If provided and create_audience is true, the created audience is automatically linked to this campaign on creation. Omit if you are not using campaign workspaces.\n"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional HTTPS URL to receive segment.ready or segment.failed events when the identity matching job completes. Overrides the org-level webhook URL for this request only.\n"
                  },
                  "hitl": {
                    "type": "boolean",
                    "default": false,
                    "description": "Requires an explicit confirmation step before identity resolution runs — see the two workflow sequences above. Defaults to false (upload alone resolves automatically, the existing behavior).\n"
                  },
                  "match_level": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "I",
                        "H",
                        "D",
                        "S",
                        "A"
                      ]
                    },
                    "description": "Filter which match levels are accepted for this job. `I` = individual, `H` = household, `D` = digital (email/phone only match), `S` = spatial (nearby-address proximity match), `A` = address-level. Defaults to all 5 levels (`['I', 'H', 'D', 'S', 'A']`) if omitted. This only affects which rows match and at what level — field selection (`field_list`/`template_id`) happens separately, per delivery (`POST /v1/audiences/{id}/deliveries` or `POST /v1/match/{id}/deliveries`), where individual-level attributes are nulled out per row for rows that didn't match at a level that qualifies for individual-level data.\n"
                  },
                  "shard_count": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50,
                    "default": 1,
                    "description": "Set > 1 for same-format shards. Every CSV shard must include the same header row, and all shards must share schema and compression. Returns upload_urls instead of upload_url.\n"
                  }
                }
              },
              "examples": {
                "minimal": {
                  "summary": "Minimal request — omit the body entirely or send `{}` (CSV, auto-named)",
                  "value": {}
                },
                "named": {
                  "summary": "Named match job — CSV",
                  "value": {
                    "name": "Q2 2026 CRM Refresh"
                  }
                },
                "with_campaign": {
                  "summary": "Match job linked to a campaign",
                  "value": {
                    "name": "Q2 2026 CRM Refresh",
                    "campaign_id": "camp_abc123"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "File match job created — upload your file to the returned upload_url to proceed.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "match_id",
                    "segment_id",
                    "name",
                    "status",
                    "upload_expires_at",
                    "expires_at",
                    "file_format",
                    "compression"
                  ],
                  "properties": {
                    "match_id": {
                      "type": "string",
                      "description": "Backward-compatible alias of segment_id."
                    },
                    "segment_id": {
                      "type": "string",
                      "description": "Unique ID for this matched segment. Poll `GET /v1/segments/{segment_id}` until status is active.\n"
                    },
                    "audience_id": {
                      "type": "string",
                      "description": "ID of the auto-created audience wrapper. Present when create_audience is true. Use for deliveries once the segment is active.\n"
                    },
                    "integration_run_id": {
                      "type": "string",
                      "pattern": "^ir_[A-Za-z0-9_-]{40}$",
                      "description": "Present when X-Integration-Execution-Key created a private OAuth partnership run.\n"
                    },
                    "name": {
                      "type": "string",
                      "description": "Label assigned to the job (supplied or auto-generated).\n"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending"
                      ],
                      "description": "Always `pending` on creation — the segment is awaiting file upload. Poll `GET /v1/segments/{segment_id}` — it will show `status: pending` until identity resolution completes, then `status: active`.\n"
                    },
                    "upload_url": {
                      "type": "string",
                      "description": "30-minute signed upload URL. Upload your file to this URL with the `Content-Type` matching your `file_format`. No `Authorization` header required. Present when `shard_count` is 1 (the default) — absent when sharded, use `upload_urls` instead.\n"
                    },
                    "upload_urls": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      },
                      "description": "30-minute signed upload URLs, one per shard, index-ordered. Present only when `shard_count` > 1 was requested — `upload_url` is absent. Every CSV shard must include the same header row.\n"
                    },
                    "upload_expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "description": "ISO timestamp when `upload_url`/`upload_urls` expires (30 minutes from creation).\n"
                    },
                    "expires_at": {
                      "type": "string",
                      "description": "ISO date — 90-day hard expiry for this segment. After this date the segment and associated files are permanently deleted.\n"
                    },
                    "file_format": {
                      "type": "string",
                      "enum": [
                        "csv",
                        "avro",
                        "json",
                        "jsonl"
                      ],
                      "description": "Confirmed file format for this job.\n"
                    },
                    "compression": {
                      "type": "string",
                      "enum": [
                        "none",
                        "gzip"
                      ],
                      "description": "Confirmed input artifact compression."
                    }
                  }
                },
                "examples": {
                  "csv_job": {
                    "summary": "CSV job with audience (default)",
                    "value": {
                      "match_id": "seg_csv123",
                      "segment_id": "seg_csv123",
                      "audience_id": "aud_csv123",
                      "name": "Q2 2026 CRM Refresh",
                      "status": "pending",
                      "upload_url": "https://storage.googleapis.com/cf-uploads/enrichment-uploads/org_x/seg_csv123/input.csv?X-Goog-Signature=...",
                      "upload_expires_at": "2026-06-22T19:30:00.000Z",
                      "expires_at": "2026-09-27",
                      "file_format": "csv",
                      "compression": "none"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "description": "`IDEMPOTENCY_KEY_CONFLICT` — the `Idempotency-Key` header was already used with a different request body. `IDEMPOTENCY_REQUEST_IN_PROGRESS` — another request with this key is in flight; retry after the seconds in `Retry-After`.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "error",
                    "code",
                    "message"
                  ],
                  "properties": {
                    "error": {
                      "type": "string"
                    },
                    "code": {
                      "type": "string",
                      "enum": [
                        "IDEMPOTENCY_KEY_CONFLICT",
                        "IDEMPOTENCY_REQUEST_IN_PROGRESS"
                      ]
                    },
                    "message": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        }
      },
      "get": {
        "summary": "List file match jobs",
        "operationId": "listFileMatchJobs",
        "tags": [
          "Enrichment"
        ],
        "description": "List file match jobs for the org, newest first. Cursor-paginated. Optional `status` filter. Job history persists even after the underlying segment or its linked audience is deleted from Library — deleted jobs are not excluded from this list.\nRequires 'discovery' scope.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 30,
              "maximum": 100
            }
          },
          {
            "in": "query",
            "name": "before",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Exclusive upper bound cursor on created_at (ISO 8601)"
          },
          {
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "active",
                "failed",
                "archived",
                "expired"
              ]
            },
            "description": "Filter by segment status"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of file match jobs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileMatchJobList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/match/{id}/deliveries": {
      "description": "Convenience wrapper around POST /v1/audiences/{id}/deliveries for match jobs. The {id} is the match_id (segment document ID) returned by POST /v1/match/file — the server resolves the audience that wraps the segment and forwards the delivery. Always delivers to the download destination.\n",
      "get": {
        "summary": "List deliveries for a file match job",
        "operationId": "listFileMatchDeliveries",
        "tags": [
          "Deliveries"
        ],
        "description": "Lists deliveries for the audience linked to the given match (segment) ID. Resolves the audience that references the segment, then proxies to `GET /v1/audiences/{id}/deliveries` and returns its response unchanged.\n      Requires 'discovery' scope.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The `match_id` (segment document ID) returned by `POST /v1/match/file`.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "List of deliveries for the linked audience.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeliveryObject"
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "post": {
        "summary": "Deliver a file match job",
        "operationId": "deliverFileMatch",
        "tags": [
          "Deliveries"
        ],
        "description": "**Convenience wrapper around `POST /v1/audiences/{id}/deliveries` for file-based match jobs.** Always delivers to the `download` destination — the enriched file is exported to a time-limited signed URL. Omits DSP-only destinations (LiveRamp, Narrative) and their associated parameters.\n\n**Workflow (after POST /v1/match/file):**\n1. Upload your file to the `upload_url` returned by `POST /v1/match/file`. This\n   upload is what triggers identity resolution to begin — not this endpoint.\n\n2. *(Recommended)* `POST /v1/match/{segment_id}/analyze` — preview column mapping.\n   Check `unmapped_columns` and `confidence` values. If mappings look wrong, rename\n   columns in your file and re-upload before triggering delivery.\n\n3. Poll `GET /v1/match/file/{match_id}` until `matching_status` is `completed` (or use a\n   `segment.ready` webhook). Calling this endpoint before resolution finishes returns\n   `422 AUDIENCE_PENDING`.\n\n4. **`POST /v1/match/{match_id}/deliveries`** (this endpoint) — export the already-\n   resolved match to a signed download URL. Optionally supply `template_id` or\n   `field_list` to choose which enriched attributes to include in the output — if\n   neither is given, the Standard IAG attribute set is used.\n\n5. Poll **`GET /v1/match/{match_id}/deliveries`** for status — it takes the match id\n   directly, needs no audience id, and re-signs `download_urls` on every read. (The\n   audience-scoped `GET /v1/audiences/{audience_id}/deliveries/{delivery_id}` also\n   works but requires the audience id, which this wrapper is designed to let you avoid.)\n   When `status: completed`, `download_urls` (array of signed URLs) is returned.\n\n\nAll billing, dedup, reservation, and usage-outbox logic is handled identically to `POST /v1/audiences/{id}/deliveries`. Re-running the exact same (match_id × version × template) is always free.\n      Requires 'purchase' scope.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The `match_id` returned by `POST /v1/match/file`.\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "include_unmatched": {
                    "type": "boolean",
                    "default": true,
                    "description": "Include rows that did not resolve to a real graph match in the output file — these appear with null enrichment attributes and, in the exported `iag_person_id` column, a best-effort id deterministically derived from the row's own identity signals (or `null` if none qualified) rather than a real platform match. Set to `false` to receive only matched records. Defaults to `true`. Every exported row (matched or not) also carries a `row_id` column matching your original upload, for correlating a delivered row back to its source input row regardless of match status. Matched rows additionally carry `match_level` (`I`/`H`/`A`/`S`/`D`), `match_type` (e.g. `graph_name_email_match`, `vector_name_address_match`, `spatial_match`), and `match_confidence` (0–1) — all `null` (not derived) on unmatched rows.\n"
                  },
                  "template_id": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "description": "Enrichment template ID. Only `standard_iag` is valid (see `templates[]` in `GET /v1/catalog/fields`). Mutually exclusive with `field_list`. If neither is provided, the Standard IAG attribute set (all attributes with `product_usage` containing `audience`) is used.\n"
                  },
                  "field_list": {
                    "type": [
                      "array",
                      "null"
                    ],
                    "items": {
                      "type": "string"
                    },
                    "description": "Explicit list of attribute names to append — every attribute must be a valid audience attribute. Use `GET /v1/catalog/fields` to browse available attributes. Mutually exclusive with `template_id`. If neither is provided, the Standard IAG attribute set is used. Individual-level attributes come back `null` for a row whose `match_level` doesn't qualify for individual-level data — this is expected, not an error. If `iag_household_id` is requested and available for a row, it is exported in the same org-scoped, non-reversible format as `iag_person_id`.\n"
                  },
                  "webhook_url": {
                    "type": "string",
                    "format": "uri",
                    "description": "Optional HTTPS URL to receive `delivery.completed` or `delivery.failed` events for this delivery. Overrides the org-level webhook URL for this request only; if omitted, the org-configured URL is used as the fallback. Every dispatch is signed regardless of which URL is used — see the Webhooks tag for the envelope shape and signing scheme.\n"
                  },
                  "output_format": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "enum": [
                      "csv",
                      "avro",
                      "json",
                      "jsonl"
                    ],
                    "description": "Output file format. Omit to auto-detect from the uploaded file's format (avro → avro, csv → csv, json → json, jsonl → jsonl). Avro exports preserve native column types including `REPEATED`/array attributes. CSV is the default when format cannot be determined.\n"
                  },
                  "output_compression": {
                    "type": "string",
                    "enum": [
                      "none",
                      "gzip"
                    ],
                    "default": "none",
                    "description": "Explicit output artifact compression, independent of the uploaded file. gzip is supported for csv/json/jsonl. Avro uses native DEFLATE and rejects outer gzip.\n"
                  }
                }
              },
              "examples": {
                "with_template": {
                  "summary": "Deliver using the Standard IAG template",
                  "value": {
                    "template_id": "standard_iag"
                  }
                },
                "with_fields": {
                  "summary": "Deliver a custom attribute set, matched records only",
                  "value": {
                    "field_list": [
                      "age",
                      "gender",
                      "estimated_household_income"
                    ],
                    "include_unmatched": false
                  }
                },
                "with_webhook": {
                  "summary": "Deliver with webhook notification",
                  "value": {
                    "template_id": "standard_iag",
                    "webhook_url": "https://hooks.example.com/match-complete"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Delivery initiated — or a free re-delivery if this (match_id × version × template) was already completed.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeliveryObject"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/PaymentRequired"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/deliveries": {
      "get": {
        "summary": "List org-wide deliveries",
        "operationId": "listOrgDeliveries",
        "description": "Returns a paginated list of all deliveries across all audiences in the org, ordered by creation date descending. Requires 'discovery' scope.\n",
        "tags": [
          "Deliveries"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "audience_id",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Filter by specific audience ID.\n"
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "processing",
                "completed",
                "failed"
              ]
            },
            "description": "Filter by delivery status.\n"
          },
          {
            "name": "destination",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "download",
                "liveramp",
                "narrative"
              ]
            },
            "description": "Filter by delivery destination.\n"
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 25,
              "maximum": 100
            },
            "description": "Number of results per page.\n"
          },
          {
            "name": "cursor",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Pagination cursor from previous response next_cursor attribute.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated delivery list.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deliveries": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeliveryObject"
                      }
                    },
                    "total": {
                      "type": "integer"
                    },
                    "next_cursor": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/workflows/{workflow_run_id}": {
      "get": {
        "summary": "Get workflow status",
        "operationId": "getWorkflowStatus",
        "description": "Returns the current status of a similarity or propensity audience workflow run. Poll this endpoint after creating a similarity or propensity audience to track progress. Use `wait_for_workflow` (MCP) for a blocking poll, or call this endpoint directly for non-blocking status checks.\n      Requires 'discovery' scope.\n",
        "tags": [
          "Workflows"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workflow_run_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The workflow run ID returned in the POST /v1/audiences response.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "Workflow status.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workflow_run_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "running",
                        "paused",
                        "completed",
                        "failed"
                      ],
                      "description": "running = steps executing normally; paused = waiting for HITL gate response (only when hitl=true); completed = workflow finished, audience is active; failed = workflow encountered an unrecoverable error.\n"
                    },
                    "current_step": {
                      "type": "integer",
                      "description": "Current step index (1-based).\n"
                    },
                    "step_count": {
                      "type": "integer",
                      "description": "Total number of steps in the workflow.\n"
                    },
                    "paused_gate": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "description": "Present only when status is paused and hitl=true. Contains gate info for user response.\n",
                      "properties": {
                        "gate_id": {
                          "type": "string",
                          "description": "Opaque gate identifier — pass to POST /v1/workflows/{workflow_run_id}/resume.\n"
                        },
                        "message": {
                          "type": "string",
                          "description": "Human-readable message describing the decision needed.\n"
                        },
                        "options": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "description": "Valid response values — pick one and pass as `response` to the resume endpoint.\n"
                        }
                      }
                    }
                  }
                },
                "examples": {
                  "running": {
                    "summary": "Workflow in progress",
                    "value": {
                      "workflow_run_id": "wf_abc123",
                      "status": "running",
                      "current_step": 2,
                      "step_count": 5,
                      "paused_gate": null
                    }
                  },
                  "paused": {
                    "summary": "Workflow paused at HITL gate",
                    "value": {
                      "workflow_run_id": "wf_abc123",
                      "status": "paused",
                      "current_step": 3,
                      "step_count": 5,
                      "paused_gate": {
                        "gate_id": "gate_review_icp",
                        "message": "Review the generated ICP profile and confirm it matches your target audience.",
                        "options": [
                          "approve",
                          "reject",
                          "refine"
                        ]
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/workflows/{workflow_run_id}/resume": {
      "post": {
        "summary": "Respond to a HITL workflow gate",
        "operationId": "resumeWorkflow",
        "description": "Submit a user response to a paused gate, resuming the workflow from its paused state. Only relevant when the audience was created with `hitl: true`. The `response` value must match one of the `options` presented in the `paused_gate` object from `GET /v1/workflows/{workflow_run_id}`.\n      Requires 'purchase' scope.\n",
        "tags": [
          "Workflows"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "workflow_run_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "The workflow run ID to resume.\n"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "gate_id",
                  "response"
                ],
                "properties": {
                  "gate_id": {
                    "type": "string",
                    "description": "Gate identifier from the paused_gate object.\n"
                  },
                  "response": {
                    "type": "string",
                    "description": "Selected option — must match one of the values in paused_gate.options.\n"
                  }
                }
              },
              "examples": {
                "approve": {
                  "summary": "Approve the ICP profile",
                  "value": {
                    "gate_id": "gate_review_icp",
                    "response": "approve"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Gate response accepted, workflow resumed.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "workflow_run_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "running",
                        "completed"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/account/billing": {
      "get": {
        "summary": "Get the org's provider-neutral billing projection",
        "operationId": "getAccountBilling",
        "description": "Returns the source-routed billing account, active contract, and rated financial projection, platform safety overlays, Stripe payment-method readiness, and projection staleness. A parent-billed child receives only its own attributed spend and never the agency's pooled balance. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Provider-neutral billing projection",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountBillingResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/account/auto-recharge": {
      "get": {
        "summary": "Get prepaid automatic-recharge state",
        "operationId": "getAccountAutoRecharge",
        "description": "Returns the prepaid balance threshold configuration and a verified Stripe hosted-invoice URL only when customer action is required. Self-billed prepaid org owners only. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Automatic-recharge state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountAutoRechargeResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      },
      "put": {
        "summary": "Configure prepaid automatic recharge",
        "operationId": "updateAccountAutoRecharge",
        "description": "Persists recoverable intent, updates the active billing contract, and returns only a freshly re-read threshold projection. Enabling requires a ready Stripe payment method and billing address. Self-billed prepaid org owners only. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AutoRechargeUpdateRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated automatic-recharge state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountAutoRechargeResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/account/credits": {
      "get": {
        "summary": "List active paid commits and promotional credits",
        "operationId": "getAccountCredits",
        "description": "Reads active balance detail, including applicability and expiration. Parent-billed children cannot inspect their agency's pooled balances. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Active balance detail",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountCreditsResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/account/invoices": {
      "get": {
        "summary": "List invoices with hosted collection links",
        "operationId": "getAccountInvoices",
        "description": "Returns provider-rated commercial invoices and explicitly labelled administrator-created exceptional invoices, with collection status and hosted links. Parent-billed children cannot inspect agency invoices. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Invoice history",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AccountInvoicesResponse"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/account/payment-method/setup": {
      "post": {
        "summary": "Start Stripe payment-method onboarding for the billing account",
        "operationId": "createAccountPaymentMethodSetup",
        "description": "Creates a Stripe Checkout setup session for the Stripe customer linked to the source-routed billing account. It does not create or mutate a Stripe Subscription. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "success_url",
                  "cancel_url"
                ],
                "properties": {
                  "success_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  },
                  "cancel_url": {
                    "type": "string",
                    "format": "uri",
                    "maxLength": 2048
                  },
                  "tier": {
                    "type": "string",
                    "enum": [
                      "paygo",
                      "starter",
                      "growth",
                      "enterprise"
                    ]
                  },
                  "cadence": {
                    "type": "string",
                    "enum": [
                      "monthly",
                      "annual"
                    ]
                  },
                  "commitment_term_months": {
                    "type": "integer",
                    "enum": [
                      0,
                      12,
                      24,
                      36
                    ]
                  },
                  "custom_commitment_amount_cents": {
                    "type": "integer",
                    "minimum": 1
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Hosted setup session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "setup_url",
                    "session_id",
                    "billing_status"
                  ],
                  "properties": {
                    "setup_url": {
                      "type": "string",
                      "format": "uri"
                    },
                    "session_id": {
                      "type": "string"
                    },
                    "billing_status": {
                      "type": "string",
                      "const": "awaiting_payment_method"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          },
          "503": {
            "$ref": "#/components/responses/ServiceUnavailable"
          }
        }
      }
    },
    "/v1/account/topups": {
      "post": {
        "summary": "Begin an idempotent credit top-up",
        "operationId": "createAccountTopUp",
        "description": "Creates a payment-gated prepaid balance purchase. The durable attempt is keyed by request_id; promotional credit is added only after Stripe payment is verified. Purchased and promotional value expire after 12 months. Prepay, self-billed org owners only. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "request_id",
                  "amount_cents"
                ],
                "properties": {
                  "request_id": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 160
                  },
                  "amount_cents": {
                    "type": "integer",
                    "minimum": 10000,
                    "maximum": 5000000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Durable top-up attempt accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "payment_attempt_id",
                    "request_id",
                    "status",
                    "amount_cents",
                    "bonus_cents",
                    "credit_expires_after_months"
                  ],
                  "properties": {
                    "payment_attempt_id": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "action_required",
                        "paid",
                        "failed"
                      ]
                    },
                    "amount_cents": {
                      "type": "integer",
                      "minimum": 10000
                    },
                    "bonus_cents": {
                      "type": "integer",
                      "minimum": 0
                    },
                    "credit_expires_after_months": {
                      "type": "integer",
                      "const": 12
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/account/topups/{requestId}": {
      "get": {
        "summary": "Get durable top-up status",
        "operationId": "getAccountTopUp",
        "description": "Returns verified durable state for one top-up request. action_url is a Stripe hosted invoice only when customer action is required. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "requestId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Top-up status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "payment_attempt_id",
                    "request_id",
                    "status",
                    "amount_cents",
                    "bonus_cents",
                    "action_url",
                    "paid_at",
                    "last_error_code"
                  ],
                  "properties": {
                    "payment_attempt_id": {
                      "type": "string"
                    },
                    "request_id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "pending",
                        "action_required",
                        "paid",
                        "failed"
                      ]
                    },
                    "amount_cents": {
                      "type": "integer"
                    },
                    "bonus_cents": {
                      "type": "integer"
                    },
                    "action_url": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "uri"
                    },
                    "paid_at": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "format": "date-time"
                    },
                    "last_error_code": {
                      "type": [
                        "string",
                        "null"
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          }
        }
      }
    },
    "/v1/account/payment-method/portal": {
      "post": {
        "summary": "Open Stripe payment-method and invoice-history settings",
        "operationId": "createAccountPaymentMethodPortal",
        "description": "Creates a Stripe Billing Portal session limited to payment-method management and Stripe collection history. Commercial terms remain authoritative in the Infinite Audience billing contract and cannot be changed in the portal. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "return_url"
                ],
                "properties": {
                  "return_url": {
                    "type": "string",
                    "maxLength": 2048
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stripe portal session",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "portal_url"
                  ],
                  "properties": {
                    "portal_url": {
                      "type": "string",
                      "format": "uri"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/account/contract/change": {
      "post": {
        "summary": "Schedule a billing contract change",
        "operationId": "changeAccountContract",
        "description": "Schedules the selected tier and cadence at the next verified contract billing boundary. Enterprise custom terms remain platform-admin managed. Parent-billed children cannot change the agency contract. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "request_id",
                  "tier"
                ],
                "properties": {
                  "request_id": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 160
                  },
                  "tier": {
                    "type": "string",
                    "enum": [
                      "starter",
                      "growth",
                      "enterprise"
                    ]
                  },
                  "cadence": {
                    "type": "string",
                    "enum": [
                      "monthly",
                      "annual"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Contract replacement scheduled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "effective_at",
                    "scheduled_change"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "scheduled"
                    },
                    "effective_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "scheduled_change": {
                      "$ref": "#/components/schemas/BillingScheduledContractChange"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "422": {
            "$ref": "#/components/responses/UnprocessableEntity"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/account/contract/cancel": {
      "post": {
        "summary": "Schedule contract cancellation",
        "operationId": "cancelAccountContract",
        "description": "Schedules cancellation at the later of the current billing-period end or contractual commitment end. An organisation owner is required. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "request_id"
                ],
                "properties": {
                  "request_id": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 160
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Cancellation scheduled",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "effective_at",
                    "scheduled_change"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "scheduled"
                    },
                    "effective_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "scheduled_change": {
                      "$ref": "#/components/schemas/BillingScheduledContractChange"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/account/contract/cancellation/revert": {
      "post": {
        "summary": "Revert a scheduled cancellation",
        "operationId": "revertAccountContractCancellation",
        "description": "Restores the billing contract end date before cancellation becomes effective. An organisation owner is required. Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "additionalProperties": false,
                "required": [
                  "request_id"
                ],
                "properties": {
                  "request_id": {
                    "type": "string",
                    "minLength": 8,
                    "maxLength": 160
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Cancellation reverted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "status",
                    "scheduled_change"
                  ],
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "reverted"
                    },
                    "scheduled_change": {
                      "type": "null"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "502": {
            "$ref": "#/components/responses/BadGateway"
          }
        }
      }
    },
    "/v1/account/usage": {
      "get": {
        "summary": "Get aggregated usage metrics",
        "operationId": "getAccountUsage",
        "description": "Returns aggregated usage metrics for your organisation: segment and audience counts, deliveries created in the last 30 days, rated current-period spend, provider projection metadata, and compute ceiling usage as a percentage. Billing-model-agnostic — same shape for prepay and postpay/agency orgs.\n      Requires 'account' scope.\n",
        "tags": [
          "Account"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Usage metrics.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "segment_count",
                    "audience_count",
                    "deliveries_30d",
                    "spend_30d_cents",
                    "compute_usage_pct"
                  ],
                  "properties": {
                    "segment_count": {
                      "type": "integer"
                    },
                    "audience_count": {
                      "type": "integer"
                    },
                    "deliveries_30d": {
                      "type": "integer",
                      "description": "Deliveries created in the last 30 days."
                    },
                    "spend_30d_cents": {
                      "type": "integer"
                    },
                    "compute_usage_pct": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/settings/api-keys": {
      "get": {
        "summary": "List API keys for the caller's org",
        "operationId": "listSettingsApiKeys",
        "description": "Returns all API keys for the caller's org, ordered by creation date descending. Key values are never included in list responses.\n      Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of API keys.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "keys": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "scopes": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "active": {
                            "type": "boolean"
                          },
                          "created_at": {
                            "type": "string",
                            "format": "date-time"
                          },
                          "last_used": {
                            "type": [
                              "string",
                              "null"
                            ],
                            "format": "date-time"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "post": {
        "summary": "Create a self-service API key",
        "operationId": "createSettingsApiKey",
        "description": "Any authenticated user can create an API key for their own org. Allowed scopes: discovery, purchase, account. The plaintext key is returned once — it is never stored. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 80,
                    "description": "Human-readable label for the key.\n"
                  },
                  "scopes": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "discovery",
                        "purchase",
                        "account"
                      ]
                    },
                    "default": [
                      "discovery"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "API key created — raw_key shown once only.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "raw_key": {
                      "type": "string",
                      "description": "Plaintext key — copy immediately, not stored.\n"
                    },
                    "name": {
                      "type": "string"
                    },
                    "scopes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/settings/api-keys/{id}": {
      "delete": {
        "summary": "Revoke a self-service API key",
        "operationId": "revokeSettingsApiKey",
        "description": "Immediately deactivates the key. Key must belong to the caller's org. Org owner/admin can revoke any key in the org; other members can only revoke their own keys (403 SCOPE_REQUIRED otherwise). Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Key revoked.\n"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/settings/webhook": {
      "get": {
        "summary": "Get the org's webhook configuration",
        "operationId": "getWebhook",
        "description": "Returns the org's single, implicit webhook recipient — a URL with no per-event filtering, always subscribed to all four notifiable event types. For per-event-filtered, independently revocable subscriptions (e.g. one per Zap), see the Triggers tag instead. The signing secret is masked to its last 4 characters. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current webhook config (or null if not configured).\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "webhook_config"
                  ],
                  "properties": {
                    "webhook_config": {
                      "type": [
                        "object",
                        "null"
                      ],
                      "required": [
                        "url",
                        "secret"
                      ],
                      "properties": {
                        "url": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "format": "uri"
                        },
                        "secret": {
                          "type": [
                            "string",
                            "null"
                          ],
                          "description": "Masked — last 4 chars only, or null if unset"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "put": {
        "summary": "Configure the org's outbound webhook",
        "operationId": "updateWebhook",
        "description": "Sets the org-level webhook URL. When configured, the platform fires POST requests to this URL when async operations complete — audience builds (enrichment, propensity, similarity) and file delivery exports. Events fired: segment.ready, segment.failed, delivery.completed, delivery.failed. The signing secret is platform-generated, not a request field — see the Webhooks tag for the envelope shape and signing scheme, and `POST /v1/settings/webhook/secret` to rotate it. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "url"
                ],
                "properties": {
                  "url": {
                    "type": "string",
                    "format": "uri",
                    "description": "HTTPS endpoint URL to receive webhook POST requests.\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Webhook configured.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    },
                    "secret": {
                      "type": "string",
                      "description": "Plaintext signing secret — present ONLY the first time a webhook is configured for this org (the platform just generated it). A later call that only changes the URL omits this field, since the existing secret is preserved unchanged. Store it now; it is never retrievable again — use `POST /v1/settings/webhook/secret` to rotate if lost.\n"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/settings/webhook/secret": {
      "post": {
        "summary": "Rotate the org's webhook signing secret",
        "operationId": "rotateWebhookSecret",
        "description": "Rotates the org-level webhook signing secret and returns the new plaintext once. Requires a webhook URL to already be configured. No dual-secret rollover window is needed: dispatch re-reads the signing secret live on every attempt, so a delivery that fails verification under the old secret simply re-signs with the new one on its next retry and self-heals once your receiver is updated. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Secret rotated.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "secret"
                  ],
                  "properties": {
                    "secret": {
                      "type": "string",
                      "description": "Plaintext signing secret. Store it now; it is never retrievable again."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/settings/notifications": {
      "get": {
        "summary": "Get the caller's own notification preferences",
        "operationId": "getNotificationPreferences",
        "description": "Returns the caller's per-event-type email notification preferences — the user-scoped equivalent of the org-scoped webhook config, covering the same four event types. Unset keys default to `false` (opt-out) — no email is sent for any event type until the user explicitly opts in. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current notification preferences.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NotificationPreferencesResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "put": {
        "summary": "Update the caller's own notification preferences",
        "operationId": "updateNotificationPreferences",
        "description": "Updates the caller's own email notification preferences. Only these four event types are settable. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "notification_preferences"
                ],
                "properties": {
                  "notification_preferences": {
                    "$ref": "#/components/schemas/NotificationPreferences"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preferences updated.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/settings/delivery-destinations": {
      "get": {
        "summary": "Get delivery destinations",
        "operationId": "getDeliveryDestinations",
        "description": "Returns the org's current list of configured delivery destinations. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "List of configured destinations.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "destinations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/DeliveryConfig"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "put": {
        "summary": "Update delivery destinations",
        "operationId": "updateDeliveryDestinations",
        "description": "Replaces the org's configured delivery destinations (GCS, LiveRamp, Narrative). Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "destinations"
                ],
                "properties": {
                  "destinations": {
                    "type": "array",
                    "items": {
                      "$ref": "#/components/schemas/DeliveryConfig"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Destinations updated.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/settings/billing-preferences": {
      "get": {
        "summary": "Get billing preferences",
        "operationId": "getBillingPreferences",
        "description": "Returns platform-owned billing safeguards. The low-balance threshold applies to prepay accounts; budget_ceiling applies to postpay accounts. Explicit payment-gated top-ups replace the retired local automatic-overage-charge preference. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Current billing preferences.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "billing_model",
                    "low_balance_threshold",
                    "budget_ceiling"
                  ],
                  "properties": {
                    "billing_model": {
                      "type": "string",
                      "enum": [
                        "prepay",
                        "postpay"
                      ]
                    },
                    "low_balance_threshold": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Prepay only — always null for postpay."
                    },
                    "budget_ceiling": {
                      "type": [
                        "number",
                        "null"
                      ],
                      "description": "Postpay only — always null for prepay. Null also means unset (unlimited) for postpay."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "put": {
        "summary": "Update billing preferences",
        "operationId": "updateBillingPreferences",
        "description": "Updates whichever platform-owned safeguard applies to your org's own billing_model. Low-balance thresholds are prepay-only; budget ceilings are postpay-only. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "low_balance_threshold": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Prepay only."
                  },
                  "budget_ceiling": {
                    "type": [
                      "number",
                      "null"
                    ],
                    "minimum": 0,
                    "description": "Postpay only. Optional hard spend ceiling per billing cycle; null removes the ceiling (unlimited).\n"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Billing preferences updated.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid request, or the requested field doesn't apply to this org's billing model — code PREFERENCE_NOT_APPLICABLE_POSTPAY (low_balance_threshold on a postpay org) or BUDGET_CEILING_NOT_APPLICABLE_PREPAY (budget_ceiling on a prepay org).\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "preference_not_applicable_postpay": {
                    "value": {
                      "error": "Not applicable",
                      "code": "PREFERENCE_NOT_APPLICABLE_POSTPAY",
                      "message": "Low-balance alerts are prepay-only. Use budget_ceiling for postpay billing."
                    }
                  },
                  "budget_ceiling_not_applicable_prepay": {
                    "value": {
                      "error": "Not applicable",
                      "code": "BUDGET_CEILING_NOT_APPLICABLE_PREPAY",
                      "message": "Budget ceiling is a postpay-only setting."
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/settings/timezone": {
      "get": {
        "summary": "Get display timezone",
        "operationId": "getTimezoneSetting",
        "description": "Returns the org's display timezone override — an IANA timezone name (e.g. \"America/New_York\") applied to every timestamp displayed across the platform for all org members, or null if unset (each viewer falls back to their own browser-detected timezone). Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "Display timezone setting.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "timezone"
                  ],
                  "properties": {
                    "timezone": {
                      "type": [
                        "string",
                        "null"
                      ],
                      "example": "America/New_York"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "put": {
        "summary": "Set display timezone",
        "operationId": "updateTimezoneSetting",
        "description": "Sets (or clears, with `timezone: null`) the org's display timezone override. Must be a valid IANA timezone name or null. Requires 'account' scope.\n",
        "tags": [
          "Settings"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "timezone"
                ],
                "properties": {
                  "timezone": {
                    "type": [
                      "string",
                      "null"
                    ],
                    "example": "America/New_York"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Display timezone updated.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok"
                  ],
                  "properties": {
                    "ok": {
                      "type": "boolean",
                      "enum": [
                        true
                      ]
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/mcp": {
      "servers": [
        {
          "url": "https://mcp.infiniteaudience.ai",
          "description": "MCP streaming server through the shared public HTTPS load balancer"
        }
      ],
      "post": {
        "summary": "MCP session init / message dispatch",
        "operationId": "mcpPost",
        "description": "Without mcp-session-id: initialises a new MCP session (body must be an Initialize request). With mcp-session-id: dispatches a message to the existing session.\n",
        "tags": [
          "MCP"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "mcp-session-id",
            "in": "header",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Session ID returned from the Initialize response. Omit on first call."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "JSON-RPC 2.0 request envelope. On first call (no `mcp-session-id`), `method` must be `initialize`. Subsequent calls dispatch to any available MCP tool (see the **MCP — Available Tools** section below).\n",
                "required": [
                  "jsonrpc",
                  "method"
                ],
                "properties": {
                  "jsonrpc": {
                    "type": "string",
                    "enum": [
                      "2.0"
                    ],
                    "description": "JSON-RPC version. Always `\"2.0\"`."
                  },
                  "method": {
                    "type": "string",
                    "description": "RPC method name. `initialize` on session creation; a tool name (e.g. `tools/call`) on subsequent dispatches.\n"
                  },
                  "id": {
                    "description": "Client-assigned request ID echoed in the response. Omit for notifications (no response expected).\n",
                    "oneOf": [
                      {
                        "type": "string"
                      },
                      {
                        "type": "integer"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  },
                  "params": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Method-specific parameters. For `initialize`, pass protocol version and client capabilities. For `tools/call`, pass `name` (tool name) and `arguments` (tool input object).\n"
                  }
                }
              },
              "examples": {
                "initialize": {
                  "summary": "Session initialization",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 1,
                    "method": "initialize",
                    "params": {
                      "protocolVersion": "2024-11-05",
                      "capabilities": {},
                      "clientInfo": {
                        "name": "my-client",
                        "version": "1.0"
                      }
                    }
                  }
                },
                "tools_call": {
                  "summary": "Invoke a tool",
                  "value": {
                    "jsonrpc": "2.0",
                    "id": 2,
                    "method": "tools/call",
                    "params": {
                      "name": "list_audiences",
                      "arguments": {}
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "MCP response — JSON-RPC 2.0 envelope.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "jsonrpc"
                  ],
                  "properties": {
                    "jsonrpc": {
                      "type": "string",
                      "enum": [
                        "2.0"
                      ]
                    },
                    "id": {
                      "description": "Echoes the request id. Null for notifications.",
                      "oneOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "integer"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "result": {
                      "description": "Present on success; shape depends on the MCP method called."
                    },
                    "error": {
                      "type": "object",
                      "description": "Present on failure.",
                      "required": [
                        "code",
                        "message"
                      ],
                      "properties": {
                        "code": {
                          "type": "integer"
                        },
                        "message": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      },
      "get": {
        "summary": "MCP SSE stream",
        "operationId": "mcpGet",
        "description": "SSE stream for an existing MCP session.",
        "tags": [
          "MCP"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "mcp-session-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream opened — messages arrive as `text/event-stream` events.",
            "content": {
              "text/event-stream": {
                "schema": {
                  "type": "string",
                  "description": "Newline-delimited Server-Sent Events. Each `data:` line carries a JSON-RPC 2.0 message (same envelope as the POST response). Clients should parse each event's `data` attribute as JSON.\n"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      },
      "delete": {
        "summary": "MCP session termination",
        "operationId": "mcpDelete",
        "description": "Explicit session termination — cleans up server-side state.",
        "tags": [
          "MCP"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "mcp-session-id",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Session terminated"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/.well-known/agent.json": {
      "servers": [
        {
          "url": "https://a2a.infiniteaudience.ai",
          "description": "A2A discovery server through the shared public HTTPS load balancer"
        }
      ],
      "get": {
        "summary": "A2A agent card",
        "operationId": "a2aAgentCard",
        "description": "Public endpoint. Returns the Infinite Audience agent card describing capabilities, skills, and authentication instructions for A2A-compatible agent clients.\n\n**Available skills:** `discovery-count`, `discovery-lookup`, `audience-management`, `segment-management`, `catalog`, `identity-resolution`, `match-history`, `delivery-history`, `pricing`, `data-purchase`, `delivery-status`, `account-info`, `credit-purchase`, `subscription-management`, `campaign-management`.\n\nThe `match-history` skill covers listing and retrieving past micro-batch runs and file-based identity resolution jobs. Requires `discovery` scope only.\n",
        "tags": [
          "A2A"
        ],
        "security": [],
        "responses": {
          "200": {
            "description": "Infinite Audience A2A agent card.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "name",
                    "description",
                    "url",
                    "version",
                    "authentication",
                    "skills"
                  ],
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "description": {
                      "type": "string"
                    },
                    "url": {
                      "type": "string",
                      "format": "uri",
                      "description": "Base URL for A2A task submission."
                    },
                    "version": {
                      "type": "string"
                    },
                    "authentication": {
                      "type": "object",
                      "required": [
                        "schemes"
                      ],
                      "properties": {
                        "schemes": {
                          "type": "array",
                          "items": {
                            "type": "string"
                          },
                          "example": [
                            "bearer"
                          ]
                        },
                        "credentials_url": {
                          "type": "string",
                          "format": "uri",
                          "description": "Token exchange endpoint (POST /v1/auth/token)."
                        },
                        "instructions": {
                          "type": "string"
                        }
                      }
                    },
                    "skills": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "required": [
                          "id",
                          "name",
                          "description"
                        ],
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "type": "string"
                          },
                          "tags": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          },
                          "examples": {
                            "type": "array",
                            "items": {
                              "type": "string"
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/a2a/tasks": {
      "servers": [
        {
          "url": "https://a2a.infiniteaudience.ai",
          "description": "A2A tasks server through the shared public HTTPS load balancer"
        }
      ],
      "post": {
        "summary": "Create an A2A task",
        "operationId": "a2aCreateTask",
        "description": "Submits a new task to the Infinite Audience agent. Returns immediately with status \"submitted\". Poll GET /a2a/tasks/{id} for progress.\n\nThe required scope depends on the requested `skill` (enforced per skill, mirroring the REST operations each skill fronts):\n- Requires 'discovery' scope: `discovery-count`, `discovery-lookup`,\n  `match-history`, `delivery-history`, `delivery-status`, `catalog`,\n  `pricing`.\n\n- Requires 'purchase' scope: `audience-management`, `segment-management`,\n  `data-purchase`, `campaign-management`, `identity-resolution`.\n\n- Requires 'account' scope: `account-info`, `credit-purchase`,\n  `subscription-management`.\n\n\nTasks with a missing or unrecognised `skill` are rejected with 400 before any scope evaluation (fail closed).\n",
        "tags": [
          "A2A"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "skill",
                  "messages"
                ],
                "properties": {
                  "skill": {
                    "type": "string",
                    "enum": [
                      "discovery-count",
                      "discovery-lookup",
                      "match-history",
                      "delivery-history",
                      "delivery-status",
                      "catalog",
                      "pricing",
                      "audience-management",
                      "segment-management",
                      "data-purchase",
                      "campaign-management",
                      "identity-resolution",
                      "account-info",
                      "credit-purchase",
                      "subscription-management"
                    ],
                    "description": "Agent-card skill id the task is submitted under — determines the required scope (see operation description). `metadata.skill` is accepted as a fallback for A2A clients that pass hints via metadata, but the top-level attribute is preferred.\n"
                  },
                  "messages": {
                    "type": "array",
                    "description": "One or more A2A message objects. Each message has a role (\"user\" or \"agent\") and a parts array containing content fragments. Only the final user message is acted on; prior messages provide conversation context.\n",
                    "items": {
                      "$ref": "#/components/schemas/A2AMessage"
                    }
                  },
                  "metadata": {
                    "type": "object",
                    "additionalProperties": true,
                    "description": "Optional caller-defined key/value pairs stored on the task. Set `campaign_id` to provide the task with trusted context from an active campaign in the authenticated organization.\n",
                    "properties": {
                      "campaign_id": {
                        "type": "string",
                        "minLength": 1,
                        "description": "Active campaign ID used to personalize this task."
                      }
                    }
                  }
                }
              },
              "examples": {
                "audience_count": {
                  "summary": "Ask the agent to count an audience",
                  "value": {
                    "skill": "discovery-count",
                    "messages": [
                      {
                        "role": "user",
                        "parts": [
                          {
                            "type": "text",
                            "text": "How many people aged 25–34 are in California?"
                          }
                        ]
                      }
                    ]
                  }
                },
                "follow_up": {
                  "summary": "Multi-turn — provide additional context",
                  "value": {
                    "skill": "discovery-count",
                    "messages": [
                      {
                        "role": "user",
                        "parts": [
                          {
                            "type": "text",
                            "text": "How many people aged 25–34 are in California?"
                          }
                        ]
                      },
                      {
                        "role": "agent",
                        "parts": [
                          {
                            "type": "text",
                            "text": "I can help with that. Should I filter by any interests?"
                          }
                        ]
                      },
                      {
                        "role": "user",
                        "parts": [
                          {
                            "type": "text",
                            "text": "Yes, filter by homeowners only."
                          }
                        ]
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Task accepted — status is \"submitted\".\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "status",
                    "created_at"
                  ],
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "submitted"
                      ]
                    },
                    "created_at": {
                      "type": "string",
                      "format": "date-time"
                    }
                  }
                },
                "example": {
                  "id": "d3e4f5a6-7b8c-9d0e-1f2a-3b4c5d6e7f8a",
                  "status": "submitted",
                  "created_at": "2024-01-15T10:30:00Z"
                }
              }
            }
          },
          "400": {
            "description": "Missing or unrecognised `skill` (must be one of the skill ids in the agent card — GET /.well-known/agent.json), or missing/empty `messages` array.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/a2a/tasks/{id}": {
      "servers": [
        {
          "url": "https://a2a.infiniteaudience.ai",
          "description": "A2A tasks server through the shared public HTTPS load balancer"
        }
      ],
      "get": {
        "summary": "Get A2A task status",
        "operationId": "a2aGetTask",
        "description": "Poll the status of a submitted A2A task. Possible statuses: submitted, working, completed, failed, cancelled.\n      Requires 'discovery' scope.\n",
        "tags": [
          "A2A"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task with current status, messages, and result.\n",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/A2ATask"
                },
                "examples": {
                  "working": {
                    "summary": "Task still in progress",
                    "value": {
                      "id": "d3e4f5a6-7b8c-9d0e-1f2a-3b4c5d6e7f8a",
                      "status": "working",
                      "messages": [
                        {
                          "role": "user",
                          "parts": [
                            {
                              "type": "text",
                              "text": "How many homeowners aged 25-34 are there in California?"
                            }
                          ]
                        }
                      ],
                      "created_at": "2024-01-15T10:30:00Z",
                      "updated_at": "2024-01-15T10:30:05Z"
                    }
                  },
                  "completed": {
                    "summary": "Task completed with agent response",
                    "value": {
                      "id": "d3e4f5a6-7b8c-9d0e-1f2a-3b4c5d6e7f8a",
                      "status": "completed",
                      "messages": [
                        {
                          "role": "user",
                          "parts": [
                            {
                              "type": "text",
                              "text": "How many homeowners aged 25-34 are there in California?"
                            }
                          ]
                        },
                        {
                          "role": "agent",
                          "parts": [
                            {
                              "type": "text",
                              "text": "There are approximately 4.2 million homeowners aged 25–34 in California."
                            }
                          ]
                        }
                      ],
                      "result_message": {
                        "role": "agent",
                        "parts": [
                          {
                            "type": "text",
                            "text": "There are approximately 4.2 million homeowners aged 25–34 in California."
                          }
                        ]
                      },
                      "created_at": "2024-01-15T10:30:00Z",
                      "updated_at": "2024-01-15T10:30:12Z"
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/a2a/tasks/{id}/cancel": {
      "servers": [
        {
          "url": "https://a2a.infiniteaudience.ai",
          "description": "A2A tasks server through the shared public HTTPS load balancer"
        }
      ],
      "post": {
        "summary": "Cancel an A2A task",
        "operationId": "a2aCancelTask",
        "description": "Request cancellation of a submitted or in-progress task. Requires 'purchase' scope.\n",
        "tags": [
          "A2A"
        ],
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Task cancelled.\n",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "id",
                    "status"
                  ],
                  "properties": {
                    "id": {
                      "type": "string",
                      "description": "The task ID that was cancelled."
                    },
                    "status": {
                      "type": "string",
                      "enum": [
                        "cancelled"
                      ]
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Task already in terminal state (completed/failed/cancelled).\n"
          }
        }
      }
    },
    "/v1/match/runs": {
      "get": {
        "summary": "List micro-batch match runs",
        "operationId": "listMatchRuns",
        "tags": [
          "Enrichment"
        ],
        "description": "List micro-batch match history for the org, newest first. Cursor-paginated — pass `before` (ISO timestamp of the oldest record from the previous page) to fetch the next page. Unlimited depth.\nRequires 'discovery' scope.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "query",
            "name": "limit",
            "schema": {
              "type": "integer",
              "default": 30,
              "maximum": 100
            },
            "description": "Max records per page"
          },
          {
            "in": "query",
            "name": "before",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Exclusive upper bound cursor on created_at (ISO 8601)"
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of micro-batch match runs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchRunList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          }
        }
      }
    },
    "/v1/match/runs/{runId}": {
      "get": {
        "summary": "Get a micro-batch match run",
        "operationId": "getMatchRun",
        "tags": [
          "Enrichment"
        ],
        "description": "Get a single micro-batch match run by ID.\nRequires 'discovery' scope.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "runId",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Micro-batch match run",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MatchRun"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    },
    "/v1/match/file/{matchId}": {
      "get": {
        "summary": "Get a file match job",
        "operationId": "getFileMatchJob",
        "tags": [
          "Enrichment"
        ],
        "description": "Get a single file match job by segment ID. Returns 404 if the segment does not exist or is not a matched-subtype segment.\nRequires 'discovery' scope.\n",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "in": "path",
            "name": "matchId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "in": "header",
            "name": "X-Integration-Run-Id",
            "required": false,
            "schema": {
              "type": "string",
              "pattern": "^ir_[A-Za-z0-9_-]{40}$"
            },
            "description": "Required for an OAuth partnership to access a private file job; the run must belong to the same active OAuth grant.\n"
          }
        ],
        "responses": {
          "200": {
            "description": "File match job details",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FileMatchJob"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ScopeRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          }
        }
      }
    }
  },
  "webhooks": {
    "segment.ready": {
      "post": {
        "operationId": "webhookSegmentReady",
        "summary": "Segment build succeeded",
        "description": "POSTed to your webhook URL when an async segment (matched file upload, similarity, or propensity) completes successfully. Configure your URL via `PUT /v1/settings/webhook`, supply `webhook_url` per-request, or subscribe via `POST /v1/webhook-subscriptions`. `subject.id` is the segment id; `data.audience_id` is also present when an audience wrapper was auto-created alongside the segment (`create_audience: true`, which is the default for matched segments). See the Webhooks tag for the envelope shape and signing scheme.\n",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SegmentWebhookReadyPayload"
              },
              "example": {
                "id": "evt_1a2b3c4d5e6f7a8b9c0d1e2f",
                "type": "segment.ready",
                "created_at": "2026-08-26T18:04:12.000Z",
                "api_version": "2026-08-06",
                "org_id": "org_xyz",
                "subject": {
                  "kind": "segment",
                  "id": "seg_abc123",
                  "name": "High-Value Purchasers"
                },
                "data": {
                  "status": "completed",
                  "audience_id": "aud_abc123",
                  "match_count": 1200000,
                  "record_count": 1500000
                },
                "links": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Your endpoint acknowledged the event."
          }
        }
      }
    },
    "segment.failed": {
      "post": {
        "operationId": "webhookSegmentFailed",
        "summary": "Segment build failed",
        "description": "POSTed to your webhook URL when an async segment build fails due to an enrichment or workflow error. See the Webhooks tag for the envelope shape and signing scheme.\n",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SegmentWebhookFailedPayload"
              },
              "example": {
                "id": "evt_2b3c4d5e6f7a8b9c0d1e2f1a",
                "type": "segment.failed",
                "created_at": "2026-08-26T18:04:12.000Z",
                "api_version": "2026-08-06",
                "org_id": "org_xyz",
                "subject": {
                  "kind": "segment",
                  "id": "seg_abc123",
                  "name": "High-Value Purchasers"
                },
                "data": {
                  "status": "failed",
                  "failure_reason": "No usable identity columns found in uploaded file."
                },
                "links": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Your endpoint acknowledged the event."
          }
        }
      }
    },
    "delivery.completed": {
      "post": {
        "operationId": "webhookDeliveryCompleted",
        "summary": "Delivery export completed",
        "description": "POSTed to your webhook URL when a delivery export finishes and download URLs (or an equivalent destination confirmation) are available. `subject.id` is the delivery id. See the Webhooks tag for the envelope shape and signing scheme.\n",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeliveryWebhookCompletedPayload"
              },
              "example": {
                "id": "evt_3c4d5e6f7a8b9c0d1e2f1a2b",
                "type": "delivery.completed",
                "created_at": "2026-08-26T18:04:12.000Z",
                "api_version": "2026-08-06",
                "org_id": "org_xyz",
                "subject": {
                  "kind": "delivery",
                  "id": "del_abc123",
                  "name": null
                },
                "data": {
                  "status": "completed",
                  "destination": "download",
                  "output_record_count": 1200000,
                  "completed_at": "2026-08-26T18:04:10.000Z",
                  "output_format": "csv",
                  "output_compression": "gzip",
                  "output_bytes": 48213099,
                  "download_urls": [
                    "https://storage.googleapis.com/cf-exports/org_xyz/del_abc123/output.csv?X-Goog-Signature=..."
                  ]
                },
                "links": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Your endpoint acknowledged the event."
          }
        }
      }
    },
    "delivery.failed": {
      "post": {
        "operationId": "webhookDeliveryFailed",
        "summary": "Delivery export failed",
        "description": "POSTed to your webhook URL when a delivery export fails. See the Webhooks tag for the envelope shape and signing scheme.\n",
        "tags": [
          "Webhooks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeliveryWebhookFailedPayload"
              },
              "example": {
                "id": "evt_4d5e6f7a8b9c0d1e2f1a2b3c",
                "type": "delivery.failed",
                "created_at": "2026-08-26T18:04:12.000Z",
                "api_version": "2026-08-06",
                "org_id": "org_xyz",
                "subject": {
                  "kind": "delivery",
                  "id": "del_abc123",
                  "name": null
                },
                "data": {
                  "status": "failed",
                  "destination": "download",
                  "failure_reason": "Insufficient matched records to produce output."
                },
                "links": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Your endpoint acknowledged the event."
          }
        }
      }
    }
  }
}
