Migration Guide from API Key

Migrate external partners from API-key reporting to OAuth on the Vibe Developer Platform, with a dual-run period and a 2027-01-01 sunset.

Migrating the Reporting API to OAuth

The Vibe Reporting API is moving from API-key authentication (X-API-Key) to OAuth 2.0 on the Vibe Developer Platform (https://api.vibe.co). This guide walks you through the change.

The good news: the reporting data does not change. Same endpoints, same metrics, same dimensions, same filters. The migration is almost entirely about how you authenticate, plus one new required header and two renamed dimensions.

Whichever way you authenticate, the API key is replaced by an OAuth 2.0 bearer token. Vibe supports two ways to obtain one — choose the one that fits the kind of integration you're building:

  • Client credentials — best for private, first-party integrations (you pull your own account's data). You create a client_id + client_secret in Vibe and exchange it directly for a token. No user, no consent screen.
  • OAuth authorization code — best for user-facing integrations, where other Vibe users connect their account to your app. The connection is frictionless — users authorize in one click, with no credentials to create or share — and you're granted a token (plus a refresh token to renew it).

Both are valid for reporting.


What's changing — at a glance

Before (legacy)After (Developer Platform)
AuthX-API-Key: <key> headerAuthorization: Bearer <token> (OAuth 2.0)
Getting credentialsAn API key per accountAn OAuth bearer token, obtained via client credentials or an authorized OAuth app (see below)
Required headerX-Vibe-Revision: YYYY-MM-DD on every reporting call
Dimensions(see rename table below)Two dimensions renamed; everything else identical
Endpoints, metrics, filtersunchangedunchanged

If you only read one thing: swap the X-API-Key header for an Authorization: Bearer OAuth token (obtained via client credentials or an authorized OAuth app), add the X-Vibe-Revision header, and rename two dimensions. That's the whole migration.


Timeline

  1. Available now — the OAuth reporting endpoints are live. You can migrate today.
  2. Dual-run window — both X-API-Key and OAuth work until the sunset date.
  3. Sunset — 2027-01-01 — on this date, X-API-Key auth on the reporting endpoints is disabled and returns 401.

Migrate before 2027-01-01 to avoid interruption.


Step 1 — Choose your authentication method

Vibe supports two OAuth methods. Choose based on the integration you're building; both return a bearer token you send the same way.

Client CredentialsAuthorization Code
Best forPrivate, first-party integrations (you pull your own data)User-facing integrations (other Vibe users connect their account to your app)
ConnectionYou create a client_id + client_secret in Vibe and use it directlyUsers authorize in one click — frictionless, nothing to create or share
How you get a tokenExchange the secret at POST /oauth2/tokenUser authorizes in the browser, then exchange/refresh
Consent screenNoneYes (one-time per user)
Token coversOne account (the client's)One account (the one the user authorizes)

Not sure which fits? Check with your Vibe contact. The rest of this guide covers both — jump to the section that matches.

Advertiser scope

Advertiser scope is decided by the Vibe user granting access, not by your integration. When they authorize the connection (Authorization Code) or set up the credential (Client Credentials), they choose which advertisers in the account it can pull reporting for:

  • Specific advertisers — only the advertiser(s) they select.
  • All advertisers — every advertiser currently in the account.
  • All advertisers, including future ones — every current advertiser plus any added to the account later, with no need to re-authorize.

Your integration receives whatever was granted — you can't request or change the scope from the API. Reporting requests still pass advertiser_ids (or advertiser_id for purchase events), and those must fall within the granted scope.


Step 2A — Client Credentials

Get a client

You receive a client_id and client_secret for each account, either from your Vibe contact (pre-provisioned) or by creating one yourself in the developer portal (developers.vibe.co) with the reporting:read scope.

One OAuth client maps to one account, exactly like one API key mapped to one account before. If you have many accounts, you'll have many clients.

Get an access token

Exchange your client credentials for a short-lived bearer token. Authenticate with HTTP Basic (client_id as username, client_secret as password):

curl -sX POST https://api.vibe.co/oauth2/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "grant_type=client_credentials&scope=reporting:read"

Response:

{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "reporting:read"
}
  • The scope parameter is optional; if omitted you get all scopes available to the client. For reporting you only need reporting:read.
  • The token endpoint does not require the X-Vibe-Revision header.
  • Cache and reuse the token for its lifetime (expires_in seconds, ~1 hour). Do not request a new token on every API call.

Step 2B — Authorization Code

Use this method for user-facing integrations: the user authorizes your app through the browser, and you receive a token scoped to the account they authorize. Each authorization covers one account.

1. Send the user to the authorization endpoint

GET https://api.vibe.co/oauth2/auth
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://your-app.example.com/callback
  &response_type=code
  &scope=reporting:read
  &state=RANDOM_OPAQUE_VALUE

The user is prompted for consent, then redirected back to your redirect_uri with code and state query parameters. Validate that state matches what you sent (CSRF protection).

Trusted partners: consent can be pre-granted so the screen is skipped. This is arranged with Vibe per partner — talk to your Vibe contact.

2. Exchange the code for tokens

curl -sX POST https://api.vibe.co/oauth2/token \
  -u "$CLIENT_ID:$CLIENT_SECRET" \
  -d "grant_type=authorization_code&code=THE_CODE&redirect_uri=https://your-app.example.com/callback"

The response includes both an access_token and a refresh_token. Use the refresh token to obtain new access tokens without sending the user through consent again.


Step 3 — Add the X-Vibe-Revision header

Every reporting request must pin an API revision:

X-Vibe-Revision: 2026-06-01
  • The value is a date (YYYY-MM-DD) matching a published revision.
  • Pin a fixed revision in production so future backward-incompatible changes don't break you. See the API changelog.
  • An unknown or missing revision returns 400 Bad Request.
  • This header is not required on /oauth2/token or /oauth2/auth.

Step 4 — Rename two dimensions

Two reporting dimensions were renamed on the Developer Platform. Update these names in your report requests; all other dimensions and metrics are identical.

Legacy dimensionNew dimension
conv_integration_idevent_source_id
conv_integration_osevent_source_platform

Step 5 — Call the reporting endpoints

The endpoints themselves are unchanged in shape — only the base URL (https://api.vibe.co) and the headers differ.

Async reports — POST /reports

Create a report, then poll for completion and download the result.

# 1. Create
curl -sX POST https://api.vibe.co/reports \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-Vibe-Revision: 2026-06-01" \
  -H "Content-Type: application/json" \
  -d '{
        "start_date": "2026-06-01",
        "end_date": "2026-06-03",
        "advertiser_ids": ["<advertiser-uuid>"],
        "metrics": ["spend", "impressions", "number_of_purchases"],
        "dimensions": ["campaign_id", "campaign_name"],
        "granularity": "DAY",
        "format": "JSON"
      }'
# → 201 { "id": "<report-id>", "status": "CREATED", ... }
# 2. Poll (no more than once every 10 seconds)
curl -s https://api.vibe.co/reports/<report-id> \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-Vibe-Revision: 2026-06-01"
# → when "status": "READY", a pre-signed "download_url" is included

Notes:

  • Dates are YYYY-MM-DD; start_date is inclusive and end_date is exclusive. The range can span at most 45 days, but it can cover any period — including data older than 45 days ago.
  • metrics is required; dimensions is optional (omit for per-date totals).
  • download_url is valid for 24 hours after generation.
  • Reports usually complete in a few minutes. If one stays CREATED/PROCESSING past 30 minutes, resubmit it.

Purchase events — POST /reports/purchases

A synchronous query for purchase-event detail:

curl -sX POST https://api.vibe.co/reports/purchases \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "X-Vibe-Revision: 2026-06-01" \
  -H "Content-Type: application/json" \
  -d '{
        "advertiser_id": "<advertiser-uuid>",
        "start_date": "2026-06-01",
        "end_date": "2026-06-03"
      }'

Before / after

  curl -sX POST https://api.vibe.co/reports \
-   -H "X-API-Key: $API_KEY" \
+   -H "Authorization: Bearer $ACCESS_TOKEN" \
+   -H "X-Vibe-Revision: 2026-06-01" \
    -H "Content-Type: application/json" \
    -d '{ ... }'

The body is unchanged except for the two renamed dimensions.


Error handling

Reporting endpoints return Vibe's error envelope. A live 401 looks like:

{
  "error": {
    "type": "token_invalid",
    "message": "Authentication required.",
    "status": 401,
    "request_id": "821a4f0fb5afa6da8df33807ec0f7fd3"
  }
}

error.detail and error.doc_url are optional and may be absent (as above). Always log request_id — it speeds up support.

StatustypeMeaningWhat to do
400validationMalformed request or unknown X-Vibe-RevisionFix the request / pin a valid revision
401token_invalidToken missing, expired, or invalidMint a new token and retry
403insufficient_scopeToken lacks reporting:readRequest a token with reporting:read
422validationSemantic validation failed; see detail.validationFix the listed fields
500internal_errorServer errorRetry with exponential backoff

Verified against the live API: the 401 type is token_invalid. Treat the other type values above as indicative (they come from the published spec, which already diverges from the live API on the 401 value) and confirm them against your pinned revision before doing typed error handling.

Auth is checked first. An invalid/expired token returns 401 even if the request also has a bad X-Vibe-Revision — fix auth before debugging the revision header.

The token endpoint (/oauth2/token) returns the standard OAuth error envelope instead — e.g. { "error": "invalid_client", "error_description": "..." } — so off-the-shelf OAuth libraries parse it correctly.


FAQ

Do I need to change my report request bodies? Only the two renamed dimensions. Metrics, filters, granularity, attribution window, timezone, and date handling are unchanged.

How long do access tokens last? About an hour (expires_in). Cache and reuse; mint a new one when it expires. Don't request one per call.

Can one credential cover all my accounts? No. Each credential is scoped to a single account in both methods — an authorization-code token covers the one account the user authorizes, and a client-credentials client is per account. This matches the old one-key-per-account model. For multiple accounts, you'll have multiple credentials (or authorizations), one per account.

What happens on 2027-01-01? X-API-Key auth on the reporting endpoints stops working and returns 401. Migrate before then.

Where do I get help? Include the request_id from any error response and contact your Vibe support channel.