Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.interchange.io/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Data Delivery is a standing subscription that pushes log-level data (LLD) from Scope3 into a destination you own — your cloud storage, your IAM, your data pipeline. Unlike /api/v2/buyer/reporting/metrics (which returns aggregates synchronously), Data Delivery runs on a schedule, writes objects, and never makes you pull. Two objects make it work, both scoped to an advertiser:
ObjectWhat it is
Data Delivery CredentialA named handle that tells Scope3 where to write and how to authenticate. Probed asynchronously; carries a status of PENDING, VALIDATED, or FAILED.
Data Delivery OutputA standing subscription that says “ship <data type> to <credential> every <cadence> as <format> under <pathPrefix>”. Multiple Outputs can share a credential. Campaign-scoped Outputs override the advertiser default for the same data type.
Available data types: MB_DELIVERY, IMPRESSIONS, CLICKS, VAST_EVENTS, CAPI_ATTRIBUTION, MMP_POSTBACKS. Cadences: HOURLY (minute 0), DAILY (00:00 UTC), WEEKLY (00:00 UTC on syncWeeklyDay). Formats: JSONL, PARQUET, CSV. Limits: up to 20 credentials and 20 Outputs per advertiser. One Output per (dataDeliveryType, credentialName) pair per scope — to fan a data type out to two destinations, list one Output per destination.
BASE = https://api.interchange.io/api/v2/buyer
AUTH = Authorization: Bearer scope3_<your_api_key>

End-to-end shape

The high-level flow is the same regardless of destination:
1

Grant Scope3 access on your cloud

Add a bucket/container that Scope3 can write into and grant the Scope3 principal the required permissions. Details per destination below.
2

Register the credential

PUT /advertisers/:advertiserId with a dataDelivery.credentials[] entry. The credential lands as status: PENDING.
3

Wait for the Probe

Scope3 asynchronously writes a sentinel object under _scope3-probe/ and deletes it. The credential flips to VALIDATED on success or FAILED (with statusError) on failure.
4

Attach Outputs

PUT /advertisers/:advertiserId with dataDelivery.outputs[] referencing the credential by credentialName. Optionally override per campaign via PUT /campaigns/:campaignId dataDelivery.outputs[].
5

Receive data on the cadence

Each Output fires on its schedule and lands objects under <destination>/<pathPrefix>/.... Open the bucket/container to confirm.
dataDelivery.credentials and dataDelivery.outputs are full-replace arrays. The request body is the new desired state — anything not listed is archived (credentials) or cleared (Outputs). Read the current state with GET /advertisers/:advertiserId before writing. A credential cannot be archived while any live Output (advertiser- or campaign-scoped) still references it — remove or repoint the Output first.

Destination setup

1. Grant access on your bucket

Scope3 authenticates to GCS with a single service account. Grant it roles/storage.objectCreator on the bucket you want data shipped into. No keys or secrets are exchanged.Scope3 service account:
report-delivery@swift-catfish-337215.iam.gserviceaccount.com
Grant via gcloud:
gcloud storage buckets add-iam-policy-binding gs://my-scope3-data \
  --member="serviceAccount:report-delivery@swift-catfish-337215.iam.gserviceaccount.com" \
  --role="roles/storage.objectCreator"

2. Register the credential

curl -X PUT "$BASE/advertisers/12345" \
  -H "Authorization: Bearer scope3_<your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "dataDelivery": {
      "credentials": [
        {
          "name": "primary-gcs",
          "config": {
            "type": "GCS",
            "bucket": "my-scope3-data"
          }
        }
      ]
    }
  }'
name must be unique among live credentials on this advertiser. Outputs will reference it by this name.

Probe lifecycle

When a credential is created or its config changes, Scope3 kicks off an async Probe workflow keyed on the credential. The Probe writes a sentinel object to the destination, deletes it, and updates the credential row:
statusMeaning
PENDINGJust created or rotated; Probe hasn’t completed yet. Typically clears within seconds.
VALIDATEDProbe wrote and deleted a sentinel object. validatedAt reflects the run.
FAILEDProbe could not write. statusError carries a human-readable reason.
Check status with:
curl "$BASE/advertisers/12345" \
  -H "Authorization: Bearer scope3_<your_api_key>"
# inspect dataDelivery.credentials[].status / statusError
Re-run the Probe without re-submitting the credential:
curl -X POST "$BASE/advertisers/12345/data-delivery-credentials/primary-gcs/validate" \
  -H "Authorization: Bearer scope3_<your_api_key>"
Outputs can reference a credential in any status. The shipping workflow re-checks at run time, so a credential that flips to FAILED will block its Outputs until you fix it; one that flips back to VALIDATED resumes automatically.

Attaching Outputs

Outputs live alongside credentials in the same dataDelivery block. The example below ships hourly impressions and daily clicks to the same GCS credential, with each data type writing under a distinct path prefix.
curl -X PUT "$BASE/advertisers/12345" \
  -H "Authorization: Bearer scope3_<your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "dataDelivery": {
      "outputs": [
        {
          "dataDeliveryType": "IMPRESSIONS",
          "cadence": "HOURLY",
          "credentialName": "primary-gcs",
          "deliveryConfig": {
            "type": "GCS",
            "pathPrefix": "scope3/impressions/",
            "format": "PARQUET"
          }
        },
        {
          "dataDeliveryType": "CLICKS",
          "cadence": "DAILY",
          "credentialName": "primary-gcs",
          "deliveryConfig": {
            "type": "GCS",
            "pathPrefix": "scope3/clicks/",
            "format": "JSONL"
          }
        }
      ]
    }
  }'

Rules to know

  • credentialName must point at a live credential on the same advertiser. The deliveryConfig.type must match the credential’s destinationType (you can’t ship to S3 through a GCS credential).
  • One Output per (dataDeliveryType, credentialName). To send the same data type to two destinations, list two Outputs with different credentials.
  • syncWeeklyDay is required when cadence: "WEEKLY" (0 = Sunday, 6 = Saturday). Ignored for HOURLY/DAILY.
  • pathPrefix is used verbatim — leading slashes are not stripped and the prefix is not templated. End it with / if you want a directory-like layout.
  • enabled: false pauses the schedule without removing the Output. In-flight runs complete; no new runs fire until you flip it back.

Campaign-scoped overrides

For a single campaign that needs a different cadence, format, or destination than its advertiser default, set dataDelivery.outputs on the campaign:
curl -X PUT "$BASE/campaigns/99999" \
  -H "Authorization: Bearer scope3_<your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "dataDelivery": {
      "outputs": [
        {
          "dataDeliveryType": "IMPRESSIONS",
          "cadence": "HOURLY",
          "credentialName": "primary-gcs",
          "deliveryConfig": {
            "type": "GCS",
            "pathPrefix": "scope3/campaign-99999/impressions/",
            "format": "PARQUET"
          }
        }
      ]
    }
  }'
The override replaces the advertiser-scoped Output for the matching dataDeliveryType on this campaign only. Resolved Outputs in GET responses are tagged with source: "advertiser" or source: "campaign" so it’s clear which level produced each entry.

Rotation

GCS and S3

No rotating secret to manage — Scope3’s identity is fixed, so as long as the IAM grant stays in place the credential keeps working. Rotating means changing the bucket: submit a new credential with the new bucket and the existing credentials inline-array will update in place.

Azure Blob

SAS tokens carry an expiry. The expiry from the se= claim is parsed and exposed on the credential response as expiresAt — watch it and rotate ahead of time. To rotate, submit a fresh credential with the same name:
curl -X PUT "$BASE/advertisers/12345" \
  -H "Authorization: Bearer scope3_<your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "dataDelivery": {
      "credentials": [
        {
          "name": "primary-azure",
          "config": {
            "type": "AZURE_BLOB",
            "storageAccountName": "datareports",
            "containerName": "scope3-data",
            "auth": { "mode": "SAS_TOKEN", "sasToken": "<new SAS>" }
          }
        }
      ]
    }
  }'
Updating in place preserves the credentialId, so any Output that references this credential by name keeps shipping without interruption. A fresh Probe runs against the new SAS; an in-flight Probe for the old SAS is cancelled.

Troubleshooting

Credential stuck on PENDING

The Probe workflow typically completes within seconds. If it’s been more than a minute:
  1. Re-fetch the advertiser — the row may have already flipped.
  2. Call the revalidate endpoint to force a fresh Probe.
  3. If it stays PENDING, contact Scope3 support — the Probe worker may be backed up.

status: FAILED — common statusError patterns

DestinationstatusError includes…Fix
GCSpermission, 403, does not have storage.objects.createGrant roles/storage.objectCreator to report-delivery@swift-catfish-337215.iam.gserviceaccount.com on the bucket.
GCSbucket ... does not existCheck the bucket name in config.bucket matches an existing bucket in the project that hosts the SA.
S3AccessDenied, 403Confirm the bucket policy grants s3:PutObject to arn:aws:iam::948454267882:user/scope3-data-sync-service on Resource: arn:aws:s3:::<bucket>/*.
S3PermanentRedirect, region mismatchconfig.region must match the bucket’s actual region.
S3NoSuchBucketBucket name typo, or the bucket isn’t in a commercial AWS partition.
AzureAzure SAS token expired at ...The se= claim is in the past. Mint a new SAS and update the credential.
AzureAuthenticationFailedSAS signature is wrong (truncated paste, wrong sv= version, signed against a different key). Regenerate.
AzureAuthorizationPermissionMismatchSAS is valid but missing required permissions. Confirm sp includes c, w, d (sp=cwd). For Account SAS, also confirm ss=b.
AzureAuthorizationResourceTypeMismatchAccount SAS onlysrt is missing o (Object). The Probe and delivery worker perform blob-level operations, so srt must include o (e.g., srt=co). srt=s or srt=c alone will not work. Regenerate.
AzureAuthorizationFailure on PUT /<container>/_scope3-probe/<uuid>.txtService SAS was scoped to a single blob (sr=b) instead of the container (sr=c). Regenerate with az storage container generate-sas so the SAS covers any blob name under the container.
AzureContainerNotFoundcontainerName doesn’t exist on the storage account, or the SAS was issued at a different scope.

Output created but no objects landing

  1. Verify the referenced credential is VALIDATED.
  2. Check enabled on the Output — if false, the schedule is paused.
  3. For HOURLY cadence, allow a full hour boundary to pass before the first run. DAILY fires at 00:00 UTC, WEEKLY at 00:00 UTC on syncWeeklyDay.
  4. Confirm there is delivery data for the period — a campaign with zero impressions on a given day produces no objects, not an empty file.

Conflicting Outputs

If PUT returns a validation error about duplicate (dataDeliveryType, credentialName) pairs, it’s because the inline array contains two Outputs with the same data type pointing at the same credential. Use distinct credentials, or merge into a single entry.
  • Reporting Overview — pull-style aggregate metrics (the synchronous counterpart to Data Delivery).
  • Log Events — push events into Scope3 (the inbound counterpart to outbound Data Delivery).