Skip to content

feat: Add TIME pricing model for duration-based sponsorships#1021

Merged
bokelley merged 1 commit into
mainfrom
bokelley/time-pricing-model
Feb 11, 2026
Merged

feat: Add TIME pricing model for duration-based sponsorships#1021
bokelley merged 1 commit into
mainfrom
bokelley/time-pricing-model

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Closes #930

  • Adds time pricing model to support sponsorship deals priced per time unit (hour, day, week, month)
  • Total cost = fixed_price x number of time units in the campaign flight
  • Complements flat_rate (fixed total cost) with duration-based pricing that lets buyers self-serve any campaign length
  • Includes optional min_duration/max_duration constraints

Changes

New schema:

  • static/schemas/source/pricing-options/time-option.json

Updated schemas:

  • pricing-model.json enum — added "time"
  • pricing-option.json oneOf — added $ref to time-option
  • index.json registry — added time-option entry

Updated docs:

  • pricing-models.mdx — full Time section with example, parameters, calculation table, comparison with flat rate
  • media-products.mdx — added Time to supported models list, fixed CPA naming inconsistency, removed phantom CPL entry

Industry alignment

  • Maps to GAM's CPD (Cost Per Day) CostType for Sponsorship line items
  • Matches IAB OpenDirect v2.0 CPD RateType
  • Covers standard direct-sales IOP rate structures (per-day, per-week, per-month)
  • Correctly does not attempt to cover DOOH per-slot pricing (handled by CPM + multiplier or flat_rate with DOOH parameters)

Test plan

  • All 293 tests pass
  • TypeScript typecheck passes
  • Schema validation confirms cross-references resolve
  • Doc example validates against schema
  • Pre-push link check passes (no broken links)
  • Verify docs render correctly on Mintlify preview

🤖 Generated with Claude Code

Add a new `time` pricing model that charges per time unit (hour, day,
week, or month), enabling self-serve sponsorship buys where total cost
scales with campaign duration. This complements `flat_rate` which
represents a fixed total cost regardless of duration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@bokelley bokelley merged commit 098fce2 into main Feb 11, 2026
7 checks passed
bokelley added a commit that referenced this pull request Apr 26, 2026
* feat(training-agent): GCP KMS-backed webhook signing

Routes the training-agent's outbound webhook signing through a GCP KMS
SigningProvider (5.21.0 surface added per upstream issue #1020 / PR
#1021). Private webhook-signing key material no longer enters process
memory in production.

AdCP requires distinct key material per signing purpose
(docs/guides/SIGNING-GUIDE.md § Key separation), so this provisions a
second KMS cryptoKeyVersion separate from the request-signing key. New
Fly secret: GCP_KMS_WEBHOOK_KEY_VERSION. The shared GCP_SA_JSON covers
IAM for both.

Refactors server/src/security/gcp-kms-signer.ts into a factory pattern:
- getRequestSigningProvider()  — outbound AdCP request signing
- getWebhookSigningProvider() — webhook signing
Shared init / tripwire / lazy-singleton / in-flight-dedup logic.

expected-public-key.ts now exports both committed PEMs and KIDs:
- aao-signing-2026-04 (request-signing)
- aao-webhook-2026-04 (webhook-signing)

The published JWKS at /.well-known/jwks.json advertises both keys with
their respective adcp_use values; receivers enforce purpose at that
field per the AdCP spec.

training-agent/webhooks.ts:
- New synchronous SigningProvider wrapper that exposes the static wire
  identity (keyid/algorithm/fingerprint from constants) immediately and
  defers KMS resolution to first sign() call. Lets createAdcpServer's
  sync `webhooks` config accept a provider whose KMS round-trip hasn't
  happened yet.
- New getWebhookSigningMaterial() returning the discriminated SDK shape
  (signerKey | signerProvider).
- Dev fallback unchanged: ephemeral key generation or
  WEBHOOK_SIGNING_KEY_JWK when KMS env unset.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(webhooks): preserve deriveWebhookOperationId structured key format

The earlier rewrite of training-agent/webhooks.ts replaced the structured
operation_id (`${principal}|${toolName}.${entityId}`) with a hex hash —
the existing tests in
server/tests/unit/training-agent-webhook-operation-id.test.ts assert on
the structured format (e.g. 'p|create_media_buy.mb_1') and broke.

Restored main's webhooks.ts as the base and surgically added only the
KMS path on top:
- Imports for getWebhookSigningProvider + WEBHOOK_SIGNING_KID/PEM
- KMS_WEBHOOK_ENV constant, WebhookMaterial discriminated union
- buildKmsWebhookProviderWrapper(): sync SigningProvider that defers
  KMS resolution to first sign() call so createAdcpServer's sync
  webhooks config can take a provider whose KMS init hasn't happened
- ensureMaterial() picks KMS when GCP_KMS_WEBHOOK_KEY_VERSION set,
  otherwise falls back to existing ephemeral / WEBHOOK_SIGNING_KEY_JWK
- getWebhookSigningMaterial() returns { signerKey } or { signerProvider }
- getPublicJwks/getWebhookEmitter use the discriminated material

deriveWebhookOperationId, generateEphemeralKey, loadConfiguredKey, the
TOOL_TO_PROTOCOL map, and all other surface preserved verbatim.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Add TIME pricing model for sponsorship-based advertising

1 participant