API reference

Base URL: your Waveband host (https://waveband.io or your white-label domain). Every operation below is generated from the same registry as the OpenAPI 3.1 document, which carries the JSON Schema of every request body. To find the API call for something you do in the console, see Where can I do this?

Authentication

Send Authorization: Bearer wbk_... on every request. Keys are created in Console → Developers with exactly the scopes they need, optionally pinned to a single workspace. The key is shown once. MCP clients can use OAuth instead (see MCP & AI agents); an OAuth access token (rto_...) works on these endpoints too, with the scopes granted at sign-in.

Developer settings page with API keys, webhooks, and OAuth clients
Console → Developers: API keys, webhook endpoints, and OAuth clients.

Conventions

  • Money is integer USD cents (budgetUsdMinor: 500000 is $5,000). Ids are UUIDs. Dates are YYYY-MM-DD; timestamps are ISO 8601.
  • Scopes. Each operation lists the scopes it needs; when it lists several, the key must hold all of them. Some bodies need an extra scope, noted on the operation (for example targeting changes need targeting:write).
  • Idempotency. Operations marked supports Idempotency-Key accept an Idempotency-Key header. A retry with the same key and body replays the first response; the same key with a different body returns 409 WAVEBAND_IDEMPOTENCY_CONFLICT. So does a retry while the first request is still running; if that request never finishes, the key frees up after 5 minutes.
  • Lists return { items }. Most return the full set; lists that can grow large take limit (and offset where shown) query parameters, listed on the operation.
  • Errors are JSON { error: { code, message, details } } with a stable code: 400 validation, 401 bad key, 403 missing scope, 404, 409 conflict. See the error reference.
  • Rate limit: 240 requests per minute per key; beyond that, 429 WAVEBAND_RATE_LIMITED. Retry after a minute.
  • Delivery sync is asynchronous: writes return at once and reach delivery shortly after. Watch sync.succeeded.v1 / sync.failed.v1 on webhooks.

Scopes

The full list (append-only; removing a scope would be a breaking change):

workspaces:readworkspaces:writecampaigns:readcampaigns:writeline_items:readline_items:writetargeting:readtargeting:writecreatives:readcreatives:writeevents:readevents:writesegments:readsegments:writeaudiences:readaudiences:writereports:readreports:writemetrics:readdatasets:readdatasets:exportbilling:readbilling:writewebhooks:readwebhooks:writeapi_keys:readapi_keys:writeautomations:readautomations:writebranding:readbranding:writedomains:readdomains:writeai:invokediagnostics:readdiagnostics:runadmin:readadmin:writecatalog:readcatalog:writeapprovals:readapprovals:writemembers:readmembers:write

Operations

Campaigns, targeting, and creatives

get/api/v1/workspaces

List workspaces

Required scope: workspaces:read · 200 Workspaces visible to the key.

post/api/v1/workspaces

Create a workspace

Required scope: workspaces:write · JSON body · 201 The created workspace. · supports Idempotency-Key

get/api/v1/workspaces/{id}

Get a workspace

  • id (path): uuid

Required scope: workspaces:read · 200 The workspace, including its advertiser domain, IAB category, and disclosure level.

patch/api/v1/workspaces/{id}

Update workspace settings

Advertiser domain and IAB category (set together; delivery needs them before a campaign can run, and they apply to every campaign in the workspace) and/or disclosureLevel, what client roles see of spend: billed_rates (spend at your rate multiplier), percent_delivery (no spend), or full_transparency (actual spend). Client roles cannot change settings.

  • id (path): uuid

Required scope: workspaces:write · JSON body · 200 The updated workspace.

get/api/v1/campaigns

List campaigns in a workspace

  • workspaceId (query, required)
  • status (query): Filter by status.

Required scope: campaigns:read · 200 Campaigns ordered by creation date.

post/api/v1/campaigns

Create a campaign

Required scope: campaigns:write · JSON body · 201 The created campaign (sync to delivery is asynchronous). · supports Idempotency-Key

get/api/v1/campaigns/{id}

Get a campaign

  • id (path): uuid

Required scope: campaigns:read · 200 The campaign.

patch/api/v1/campaigns/{id}

Update a campaign

Also used for pause/resume via the status field.

  • id (path): uuid

Required scope: campaigns:write · JSON body · 200 The updated campaign.

get/api/v1/line-items

List line items in a campaign

  • campaignId (query, required)

Required scope: line_items:read · 200 Line items ordered by creation date.

post/api/v1/line-items

Create a line item

Targeting other than the default (untargeted) also needs the targeting:write scope.

Required scope: line_items:write · JSON body · 201 The created line item. · supports Idempotency-Key

get/api/v1/line-items/{id}

Get a line item

  • id (path): uuid

Required scope: line_items:read · 200 The line item, including its targeting.

patch/api/v1/line-items/{id}

Update a line item

Budget, bid, pacing, targeting, and status (pause/resume/activate) changes. `targeting` replaces the whole object, except that an omitted `adsTxtOnly` or `defaultExchanges` keeps its current value. A body that includes `targeting` also needs the targeting:write scope.

  • id (path): uuid

Required scope: line_items:write · JSON body · 200 The updated line item.

post/api/v1/line-items/{id}/creatives

Attach a creative to a line item

  • id (path): uuid

Required scope: line_items:write · JSON body · 201 The association.

get/api/v1/line-items/{id}/creatives

List the creatives on a line item

  • id (path): uuid

Required scope: line_items:read · 200 Attached creatives (removed ones excluded).

delete/api/v1/line-items/{id}/creatives

Remove a creative from a line item

A running line item must keep at least one creative: attach the replacement first, or pause it.

  • id (path): uuid
  • creativeId (query, required)

Required scope: line_items:write · 200 No content.

get/api/v1/creatives

List creatives in a workspace

  • workspaceId (query, required)

Required scope: creatives:read · 200 Creatives ordered by creation date.

post/api/v1/creatives

Create a creative

Assets are URL-based. Video/audio sync asynchronously. HTML5 may use an inline `html` body (Creative Studio / Waveforms produce ordinary html5 creatives this way; there is no separate Waveform type). For Soundframe (audio-led video), omit assets and set render.kind=audio_led_video. Supply audioSource and/or script (with targetDurationSeconds + voicePreset), and imageSource and/or imagePrompt (script alone can drive both); the worker generates gaps, renders MP4 (≤90s), then syncs as a normal video creative.

Required scope: creatives:write · JSON body · 201 The created creative. · supports Idempotency-Key

get/api/v1/creatives/{id}

Get a creative

  • id (path): uuid

Required scope: creatives:read · 200 The creative with processing status.

get/api/v1/reference/{kind}

Look up targeting reference values

Values for targeting fields that take ids: metros (DMA codes), regions, cities (numeric city ids), inventorySources (exchanges), contentGenres. `q` searches labels (cities need 2+ characters); `values=a,b` returns the labels for stored values (up to 100). At most 50 results.

  • kind (path): Which reference list.
  • q (query): Search text.
  • values (query): Comma-separated values to label.

Required scope: targeting:read · 200 Matching options as { value, label } items.

post/api/v1/forecasts

Forecast available impressions

Available supply matching the targeting over the flight (UTC, from tomorrow, at most 31 days). Not a delivery promise and not unique reach. Forecast capacity is shared and limited per account per minute: when it is used up the result is status unavailable with reason busy; retry in a minute. Identical queries are cached for 30 minutes.

Required scopes (all of): line_items:read, targeting:read · JSON body · 200 status ok with impressions, low, high, days, and unaccounted settings; or status unavailable with a reason.

post/api/v1/creative-generations/quote

Price an AI ad generation

Nothing is reserved or charged. Returns the typical and maximum cost in USD cents.

Required scopes (all of): creatives:write, ai:invoke · JSON body · 200 maxUsdMinor, typicalUsdMinor, creatives, and availableUsdMinor.

post/api/v1/creative-generations

Generate ads with AI

Charges the wallet: up to the quoted maximum is set aside now and settled to the actual cost when the ads are delivered. Quote first. The ads appear as processing creatives in the workspace.

Required scopes (all of): creatives:write, ai:invoke · JSON body · 201 generationId, prepaidUsdMinor, and the number of creatives. · supports Idempotency-Key

get/api/v1/creative-generations

List recent AI ad generations

  • workspaceId (query, required)

Required scope: creatives:read · 200 The 20 most recent generations, newest first.

patch/api/v1/deals/{id}

Update or archive a deal

Set status archived to archive. Line items can no longer target an archived deal.

  • id (path): uuid

Required scope: catalog:write · JSON body · 200 The updated deal.

get/api/v1/curated-deals

List curated deals you can activate

Required scope: catalog:read · 200 Platform curated deals, each marked alreadyActivated when in your library.

post/api/v1/curated-deals/{id}/activate

Activate a curated deal

Copies the curated deal into your deal library so line items can target it. Returns the existing deal when it is already there.

  • id (path): uuid

Required scope: catalog:write · JSON body · 201 The library dealId.

get/api/v1/conversion-events

List conversion events

  • workspaceId (query, required)

Required scope: events:read · 200 Conversion event definitions.

post/api/v1/conversion-events

Create a conversion event

Required scope: events:write · JSON body · 201 The created conversion event with its tracking snippet. · supports Idempotency-Key

Audiences

get/api/v1/audiences

List audience segments in a workspace

  • workspaceId (query, required)
  • status (query): Filter by status (active | archived).

Required scope: audiences:read · 200 Segments with matched counts and provisioning status.

post/api/v1/audiences

Create an audience segment

Pixel-based rules (site_visitors, converters, event_rule), uploaded lists (crm_list, ip_list), or a marketplace activation.

Required scope: audiences:write · JSON body · 201 The created segment (upstream materialization is asynchronous). · supports Idempotency-Key

get/api/v1/audiences/{id}

Get an audience segment

  • id (path): uuid

Required scope: audiences:read · 200 The segment.

delete/api/v1/audiences/{id}

Archive an audience segment

  • id (path): uuid

Required scope: audiences:write · 200 The archived segment.

post/api/v1/audiences/{id}/members

Upload members to a list segment

crm_list: emails/phones (hashed at ingest; raw values are never stored) or pre-hashed sha256 lines. ip_list: IP addresses.

  • id (path): uuid

Required scope: audiences:write · JSON body · 201 The upload batch with accepted counts.

post/api/v1/audiences/{id}/attach

Attach or detach a segment on a line item

One-click retargeting: merges the segment into the line item's audience targeting.

  • id (path): uuid

Required scopes (all of): audiences:write, targeting:write · JSON body · 200 The updated line item.

get/api/v1/marketplace-segments

Search the third-party segment marketplace

  • q (query): Optional search across name, taxonomy path, provider, and description. Omit to browse.
  • provider (query): Filter by data provider.
  • minCpm (query): Minimum data CPM in USD dollars (inclusive).
  • maxCpm (query): Maximum data CPM in USD dollars (inclusive).
  • minReach (query): Minimum provider-reported reach (inclusive).
  • sort (query): Result ordering (default path).
  • limit (query)
  • offset (query): Pagination offset (default 0).

Required scope: audiences:read · 200 { items, total }: catalog rows with data CPM and reach.

Reports, events, webhooks, and keys

get/api/v1/reports

Query metrics

Aggregated delivery/performance rows, including video/audio starts, quartiles, completes, and completionRate (completes / starts, null when nothing played). Grouped by creative, conversions are null: they are attributed per line item. format=csv streams a CSV export.

  • workspaceId (query, required)
  • from (query, required): YYYY-MM-DD inclusive.
  • to (query, required): YYYY-MM-DD inclusive.
  • groupBy (query)
  • campaignId (query)
  • format (query)

Required scope: reports:read · 200 Metric rows at the requested grain.

get/api/v1/events

List domain events

The event stream that webhooks deliver; useful for backfill and debugging.

  • entityType (query)
  • entityId (query)
  • limit (query)

Required scope: webhooks:read · 200 Events, newest first.

get/api/v1/webhooks

List webhook endpoints

Required scope: webhooks:read · 200 Registered endpoints (secrets omitted).

post/api/v1/webhooks

Create a webhook endpoint

The signing secret is returned once. Verify deliveries via X-Waveband-Signature: sha256=HMAC(secret, body).

Required scope: webhooks:write · JSON body · 201 The endpoint including its one-time signing secret. · supports Idempotency-Key

delete/api/v1/webhooks/{id}

Delete a webhook endpoint

  • id (path): uuid

Required scope: webhooks:write · 200 Deletion confirmation.

get/api/v1/webhook-deliveries

List webhook deliveries

  • endpointId (query)
  • limit (query)

Required scope: webhooks:read · 200 Delivery attempts with status and response codes.

post/api/v1/webhook-deliveries/{id}/replay

Replay a webhook delivery

  • id (path): uuid

Required scope: webhooks:write · 200 The re-queued delivery.

get/api/v1/api-keys

List API keys

Required scope: api_keys:read · 200 Keys with prefixes only (never full secrets).

post/api/v1/api-keys

Create an API key

Scopes cannot exceed the calling key's scopes. The key is returned once.

Required scope: api_keys:write · JSON body · 201 The created key including its one-time plaintext.

delete/api/v1/api-keys/{id}

Revoke an API key

  • id (path): uuid

Required scope: api_keys:write · 200 Revocation confirmation.

Agency growth and Studio

get/api/v1/rate-cards

List rate cards

Required scope: catalog:read · 200 The tenant's rate cards (client-facing CPM by channel).

post/api/v1/rate-cards

Create a rate card

The first active card becomes the tenant default; isDefault switches it.

Required scope: catalog:write · JSON body · 201 The created rate card. · supports Idempotency-Key

patch/api/v1/rate-cards/{id}

Update a rate card

Edit name, description, or entries; isDefault true makes it the tenant default; status archived retires it.

  • id (path): uuid

Required scope: catalog:write · JSON body · 200 The updated rate card.

get/api/v1/packages

List productized packages

  • status (query)

Required scope: catalog:read · 200 Sellable packages with targeting, floors, and pricing.

post/api/v1/packages

Create a package

Required scope: catalog:write · JSON body · 201 The created package. · supports Idempotency-Key

get/api/v1/packages/{id}

Get a package

  • id (path): uuid

Required scope: catalog:read · 200 The package with targeting, floors, and pricing.

patch/api/v1/packages/{id}

Update a package

Only the fields sent change. The channel is fixed; status archived retires it.

  • id (path): uuid

Required scope: catalog:write · JSON body · 200 The updated package.

post/api/v1/playbooks/apply

Apply a playbook as a package

Send either { vertical } for a vertical playbook or { supplyPlaybookId } for a supply playbook. Creates a tenant-owned package you can then edit.

Required scope: catalog:write · JSON body · 201 The created package. · supports Idempotency-Key

post/api/v1/packages/{id}/instantiate

Instantiate a package into a draft campaign

Creates a draft campaign + line item priced from the package or default rate card.

  • id (path): uuid

Required scopes (all of): catalog:read, campaigns:write · JSON body · 201 The created campaignId, lineItemId, and applied CPM.

get/api/v1/deals

List curated deals

  • workspaceId (query)
  • status (query)

Required scope: catalog:read · 200 The tenant's curated deal library.

post/api/v1/deals

Add a deal to the library

Registers an SSP-issued deal ID; provisioning to delivery is asynchronous.

Required scope: catalog:write · JSON body · 201 The created deal. · supports Idempotency-Key

get/api/v1/proposals

List proposals in a workspace

  • workspaceId (query, required)

Required scope: campaigns:read · 200 Proposals with status and public share tokens.

get/api/v1/proposals/{id}

Get a proposal

  • id (path): uuid

Required scope: campaigns:read · 200 The proposal with items, status, signature, and share token.

post/api/v1/proposals

Create a proposal

Line items priced in USD minor units; share at /p/{token} once sent.

Required scope: campaigns:write · JSON body · 201 The created proposal (status draft). · supports Idempotency-Key

post/api/v1/proposals/{id}/send

Mark a proposal sent

Activates the public microsite for viewing and click-to-sign.

  • id (path): uuid

Required scope: campaigns:write · 200 The proposal with status sent.

post/api/v1/media-plans/preview

Price a media plan

Send tiers of channel splits (percent of each tier budget). Budgets, CPMs from the default rate card (baseline when unpriced), and impression forecasts are computed. Nothing is saved.

Required scope: campaigns:read · JSON body · 200 { tiers } with priced lines.

get/api/v1/media-plans

List media plans in a workspace

  • workspaceId (query, required)

Required scope: campaigns:read · 200 Saved media plans, newest first.

post/api/v1/media-plans

Save a media plan

Same splits as the preview plus workspace, name, and brief. Pricing is computed server-side, never taken from the request.

Required scope: campaigns:write · JSON body · 201 The saved media plan with priced tiers. · supports Idempotency-Key

get/api/v1/media-plans/{id}

Get a media plan

  • id (path): uuid

Required scope: campaigns:read · 200 The media plan with priced tiers.

get/api/v1/book

Book overview

Every client workspace with health score and band, 14-day spend against the prior 14 days, active campaigns, and pending change requests; plus cross-tenant channel benchmarks (shown only when enough tenants contribute).

Required scope: reports:read · 200 { clients, benchmarks }

post/api/v1/studio/display-sets

Generate a Studio display set

AI writes the headline, subline, and call to action from the brief; html5 creatives are rendered for each size (or the chosen Waveform). AI use draws on the tenant's AI funding.

Required scopes (all of): creatives:write, ai:invoke · JSON body · 201 { creativeIds, sizes } · supports Idempotency-Key

Billing, automation, and client work

get/api/v1/billing

Get billing position

Billing mode, wallet balance and the amount available to fund delivery (prepaid wallet), unbilled balance, and plan limits with current usage.

Required scope: billing:read · 200 The tenant's billing overview.

get/api/v1/billing/ledger

List ledger entries

Newest first. Charges are positive, payments and credits negative.

  • limit (query): Default 100.
  • offset (query): Rows to skip.

Required scope: billing:read · 200 Ledger entries.

post/api/v1/billing/top-up-links

Create a wallet top-up link

Returns a hosted checkout URL that a person opens to pay. Creating the link charges nothing; the wallet is credited once payment completes. New accounts have a top-up limit in their first 30 days.

Required scope: billing:write · JSON body · 201 The checkout url and amount. · supports Idempotency-Key

get/api/v1/automations

List automations in a workspace

  • workspaceId (query, required)

Required scope: automations:read · 200 Autopilot automations with goal, cadence, policy, and status.

post/api/v1/automations

Create an automation

Required scope: automations:write · JSON body · 201 The created automation. · supports Idempotency-Key

patch/api/v1/automations/{id}

Update an automation

Also pauses (status "paused", the kill switch) or resumes it.

  • id (path): uuid

Required scope: automations:write · JSON body · 200 The updated automation.

get/api/v1/decisions

List decisions

The explainable log of automation and agent decisions; proposed ones await approval.

  • workspaceId (query, required)
  • status (query)
  • limit (query): Default 100.

Required scope: automations:read · 200 Decisions, newest first.

post/api/v1/decisions/{id}/approve

Approve a decision

Applies a proposed decision. Tokens bound to an agent policy get 403. Audience decisions also need audiences:write.

  • id (path): uuid

Required scope: automations:write · 200 The updated decision.

post/api/v1/decisions/{id}/reject

Reject a decision

Rejects a proposed decision; nothing is applied.

  • id (path): uuid

Required scope: automations:write · 200 The updated decision.

post/api/v1/decisions/{id}/undo

Undo a decision

Restores the previous values of an applied decision. Audience decisions also need audiences:write.

  • id (path): uuid

Required scope: automations:write · 200 The updated decision.

post/api/v1/share-links

Create a report share link

A read-only report URL that works without signing in. Default expiry is 30 days.

Required scope: reports:write · JSON body · 201 The share link id, url, and expiry. · supports Idempotency-Key

post/api/v1/reports/narrative

Narrate a report

A plain-English summary of what happened, why, and what to do next. Runs an AI model; usage on platform models is billed.

Required scopes (all of): reports:read, ai:invoke · JSON body · 200 The narrative and the number of report rows it covers.

get/api/v1/case-studies

List case studies in a workspace

  • workspaceId (query, required)

Required scope: catalog:read · 200 Case studies, newest first.

post/api/v1/case-studies

Generate a case study

Computes the campaign's real delivery metrics and has an AI model write the story around them; the numbers are never model output. Usage on platform models is billed.

Required scopes (all of): campaigns:read, reports:read, catalog:write, ai:invoke · JSON body · 201 The saved case study. · supports Idempotency-Key

get/api/v1/case-studies/{id}

Get a case study

  • id (path): uuid

Required scope: catalog:read · 200 The case study with sections and metrics.

get/api/v1/change-requests

List change requests

  • workspaceId (query)
  • status (query)

Required scope: approvals:read · 200 Client change requests, newest first.

post/api/v1/change-requests

Submit a change request

Proposes a campaign change (pause, activate, budget) for the agency to review.

Required scopes (all of): approvals:write, campaigns:read · JSON body · 201 The pending change request. · supports Idempotency-Key

post/api/v1/change-requests/{id}/review

Review a change request

Approving applies the change with the reviewer's permissions and guardrails.

  • id (path): uuid

Required scope: campaigns:write · JSON body · 200 The reviewed change request.

get/api/v1/marketplace-providers

List audience marketplace providers

Required scope: audiences:read · 200 Providers with segment counts, and catalog freshness.

Team and brand

get/api/v1/members

List people

Everyone with access to the tenant: staff and client logins. Workspace-bound people are listed only when the key can see their workspace.

Required scope: members:read · 200 People with membershipId, email, role, and workspaceId.

post/api/v1/members

Invite a person

Creates or reuses the user by email and grants a role. Client roles need a workspaceId. The person signs in with an emailed code. Rejected when they already have access (change their role instead).

Required scope: members:write · JSON body · 201 The new membership (membershipId, userId, email, role, workspaceId). · supports Idempotency-Key

patch/api/v1/members/{id}

Change a person's role

Replaces the role and workspace binding of a membership. Send workspaceId null for tenant-wide access. You cannot change your own role.

  • id (path): uuid

Required scope: members:write · JSON body · 200 The updated membership.

delete/api/v1/members/{id}

Remove a person

Revokes the membership. You cannot remove your own access.

  • id (path): uuid

Required scope: members:write · 200 { deleted: true }

post/api/v1/client-invites

Invite a client login

Grants a client role (viewer, approver, collaborator) bound to one workspace. Client logins see only that workspace.

Required scope: members:write · JSON body · 201 The new client membership. · supports Idempotency-Key

get/api/v1/teams

List teams

Required scope: members:read · 200 Teams with their staff role, members, and workspace assignments.

post/api/v1/teams

Create a team

A team gives a group of staff one role across a set of client workspaces.

Required scope: members:write · JSON body · 201 The created team. · supports Idempotency-Key

patch/api/v1/teams/{id}

Change team members and workspaces

Adds and removes members (people already in the tenant, staff only) and, when workspaceIds is sent, replaces the team's workspaces. All changes apply together or not at all.

  • id (path): uuid

Required scope: members:write · JSON body · 200 The team with its members and workspaces.

delete/api/v1/teams/{id}

Delete a team

  • id (path): uuid

Required scope: members:write · 200 { deleted: true }

get/api/v1/branding

Get branding

Required scope: branding:read · 200 Product name, logos, colors, skin, support and email settings.

patch/api/v1/branding

Update branding

Changes only the fields sent. Image fields take an https URL; uploading logo files is done in the console.

Required scope: branding:write · JSON body · 200 The full branding record after the change.

get/api/v1/domains

List custom domains

Required scope: domains:read · 200 Domains with status and the DNS records to add.

post/api/v1/domains

Add a custom domain

Registers a hostname for the white-label console. Add every record in verificationRecords at your DNS provider; status moves from pending_dns to pending_ssl to active.

Required scope: domains:write · JSON body · 201 The domain with status and verificationRecords (type, name, value, purpose). · supports Idempotency-Key

get/api/v1/domains/{id}

Get a custom domain

Poll this for verification status; it is re-checked in the background.

  • id (path): uuid

Required scope: domains:read · 200 The domain with status, verificationRecords, lastError, and lastCheckedAt.

post/api/v1/domains/{id}/refresh

Re-check a custom domain

Checks verification now instead of waiting for the background check.

  • id (path): uuid

Required scope: domains:write · 200 The domain with its current status and verificationRecords.

delete/api/v1/domains/{id}

Remove a custom domain

  • id (path): uuid

Required scope: domains:write · 200 Deletion confirmation.

API reference · Waveband Docs