Milestone 0: import zappier billing, Verae middleware, and Zapier research

Compose-ready workspace: packages/zappier (rate card, portal, Stripe),
packages/verae-zapier-middleware (timestamp + NATS), packages/verae-zapier
(CLI app), vendor/zapier-platform, and research/zapier vendor corpus.

Gate 0 structure checks pass. Product code and research are not yet wired.
This commit is contained in:
George Lambert 2026-09-09 02:37:36 -04:00
commit b4150c8250
1364 changed files with 6814366 additions and 0 deletions

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,567 @@
openapi: 3.1.0
info:
title: Connections API
description: Register and manage webhook subscriptions for the connections your
White Label (partner) account manages. Receive a signed event whenever one of
those connections expires and needs to be reconnected.
version: v1
contact:
url: https://docs.zapier.com
servers:
- url: https://api.zapier.com
description: Production
tags:
- name: Connection Webhooks
description: Operations for managing per-partner webhook subscriptions for connection
events
security:
- OAuth: []
paths:
/connections/v1/webhooks:
get:
operationId: listConnectionWebhooks
description: List the caller's connection webhooks (active and paused).
summary: List connection webhooks
parameters:
- in: query
name: event_type
schema:
type: string
enum:
- connection.expiry_scheduled
description: Filter by event type.
- in: query
name: is_active
schema:
type: boolean
description: Filter by active/paused state.
- in: query
name: limit
schema:
type: integer
description: Number of results to return per page.
- in: query
name: offset
schema:
type: integer
description: The initial index from which to return the results.
tags:
- Connection Webhooks
security:
- OAuth:
- connection:webhook:read
- external
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PaginatedConnectionWebhookList'
description: A page of connection webhooks.
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
description: 'Validation Error: Return fields with errors.'
post:
operationId: createConnectionWebhook
description: Register an HTTPS endpoint to receive signed events for connections
managed by the caller's partner (HQ) account. The signing `secret` is returned
ONCE in this response and never again. Store it securely.
summary: Create a connection webhook
tags:
- Connection Webhooks
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionWebhookCreate'
examples:
SubscribeToConnectionExpiryEvents:
value:
event_type: connection.expiry_scheduled
callback_url: https://example.com/webhooks/zapier
summary: Subscribe to connection expiry events
required: true
security:
- OAuth:
- connection:webhook:write
- external
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionWebhookWithSecret'
description: Created. Includes the signing secret (shown once).
'400':
description: Invalid request.
'409':
description: An active webhook for this event type already exists.
callbacks:
connectionEvent:
'{$request.body#/callback_url}':
post:
description: 'When a subscribed event occurs, Zapier sends a signed
HTTPS `POST` to the `callback_url` registered on this webhook. Requests
are signed following the [Standard Webhooks](https://www.standardwebhooks.com/)
specification — verify the `webhook-signature` header using the `secret`
returned once at creation. Respond with any `2xx` status to acknowledge;
non-`2xx` responses, timeouts, and `429`s are retried with backoff.
Each request carries three headers used for signature verification.
`webhook-id` is a stable delivery ID for the event that stays constant
across retries (use it to deduplicate). `webhook-timestamp` is the
Unix epoch, in seconds, at which the event was signed. `webhook-signature`
is a space-delimited list of `v1,<base64 HMAC-SHA256>` signatures
computed over `{webhook-id}.{webhook-timestamp}.{body}`.'
summary: Connection event notification
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionWebhookEvent'
required: true
responses:
'200':
description: Event acknowledged. Any `2xx` status is accepted.
/connections/v1/webhooks/{id}:
get:
operationId: getConnectionWebhook
description: 'CRUD API for per-partner webhook subscriptions.
Scoped to the caller''s HQ account: a partner can only read and mutate
its own webhooks. ``hq_account_id`` is always derived from the
authenticated identity, never accepted from the request body.'
summary: Retrieve a connection webhook
parameters:
- in: path
name: id
schema:
type: string
format: uuid
description: UUID of the connection webhook.
required: true
tags:
- Connection Webhooks
security:
- OAuth:
- connection:webhook:read
- external
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionWebhook'
description: The connection webhook.
'404':
description: Not found.
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
description: 'Validation Error: Return fields with errors.'
patch:
operationId: updateConnectionWebhook
description: Update `callback_url` and/or pause/resume via `is_active`. `event_type`
is immutable.
summary: Update a connection webhook
parameters:
- in: path
name: id
schema:
type: string
format: uuid
description: UUID of the connection webhook.
required: true
tags:
- Connection Webhooks
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PatchedConnectionWebhookUpdate'
examples:
PauseDelivery:
value:
is_active: false
summary: Pause delivery
UpdateTheCallbackURL:
value:
callback_url: https://example.com/webhooks/zapier-v2
summary: Update the callback URL
security:
- OAuth:
- connection:webhook:write
- external
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/ConnectionWebhook'
description: Updated.
'400':
description: Invalid request.
'404':
description: Not found.
delete:
operationId: deleteConnectionWebhook
description: 'CRUD API for per-partner webhook subscriptions.
Scoped to the caller''s HQ account: a partner can only read and mutate
its own webhooks. ``hq_account_id`` is always derived from the
authenticated identity, never accepted from the request body.'
summary: Delete a connection webhook
parameters:
- in: path
name: id
schema:
type: string
format: uuid
description: UUID of the connection webhook.
required: true
tags:
- Connection Webhooks
security:
- OAuth:
- connection:webhook:write
- external
responses:
'204':
description: Deleted.
'404':
description: Not found.
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
description: 'Validation Error: Return fields with errors.'
/connections/v1/webhooks/{id}/test:
post:
operationId: createConnectionWebhookTest
description: 'Trigger a sample (synthetic) `connection.expiry_scheduled` delivery
to this webhook''s `callback_url`, so you can validate your handler and signature
verification before relying on real events. The delivered payload is a synthetic
example and is clearly marked as a test. In addition to the `webhook-id`,
`webhook-timestamp`, and `webhook-signature` headers sent on every delivery,
a test delivery also carries a `webhook-test: true` header.'
summary: Send a test delivery
parameters:
- in: path
name: id
schema:
type: string
format: uuid
description: UUID of the connection webhook.
required: true
tags:
- Connection Webhooks
security:
- OAuth:
- connection:webhook:write
- external
responses:
'202':
description: Test delivery enqueued.
'404':
description: Not found.
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/Errors'
description: 'Validation Error: Return fields with errors.'
components:
schemas:
ConnectionWebhook:
type: object
description: Output serializer for GET / PATCH responses. Secret is never returned.
properties:
id:
type: string
format: uuid
readOnly: true
description: Unique webhook identifier (UUID).
event_type:
allOf:
- $ref: '#/components/schemas/EventTypeEnum'
readOnly: true
description: 'The event type this webhook is subscribed to.
* `connection.expiry_scheduled` - Expiry Scheduled'
callback_url:
type: string
format: uri
readOnly: true
description: HTTPS URL that signed events are POSTed to.
is_active:
type: boolean
readOnly: true
description: Whether the webhook is active. Inactive webhooks do not receive
events.
created_at:
type: string
format: date-time
readOnly: true
description: ISO 8601 timestamp when the webhook was created.
updated_at:
type: string
format: date-time
readOnly: true
description: ISO 8601 timestamp when the webhook was last updated.
required:
- callback_url
- created_at
- event_type
- id
- is_active
- updated_at
ConnectionWebhookCreate:
type: object
description: Input serializer for POST /webhooks.
properties:
event_type:
allOf:
- $ref: '#/components/schemas/EventTypeEnum'
description: 'The event to subscribe to.
* `connection.expiry_scheduled` - Expiry Scheduled'
callback_url:
type: string
format: uri
description: HTTPS URL that signed events are POSTed to.
required:
- callback_url
- event_type
ConnectionWebhookEvent:
type: object
description: Outbound event payload POSTed to ``callback_url`` (documentation
only).
properties:
type:
allOf:
- $ref: '#/components/schemas/TypeEnum'
description: 'The event type. Matches the `event_type` this webhook subscribed
to.
* `connection.expiry_scheduled` - Expiry Scheduled'
expires_at:
type: string
format: date-time
description: ISO 8601 timestamp when the connection is scheduled to expire.
data:
allOf:
- $ref: '#/components/schemas/ConnectionWebhookEventData'
description: Event-specific details.
required:
- data
- expires_at
- type
ConnectionWebhookEventData:
type: object
description: The ``data`` object of an outbound connection event (documentation
only).
properties:
connection_id:
type: string
description: External (opaque) ID of the connection that triggered the event.
account_id:
type: string
description: External (opaque) ID of the account that owns the connection.
app:
type: string
description: The integration the connection belongs to, as `app_key@version`
(e.g. `SlackAPI@1.0.0`).
title:
type: string
description: Human-readable title of the connection.
required:
- account_id
- app
- connection_id
- title
ConnectionWebhookWithSecret:
type: object
description: Output serializer for POST 201. Includes the signing secret (returned
once only).
properties:
id:
type: string
format: uuid
readOnly: true
description: Unique webhook identifier (UUID).
event_type:
allOf:
- $ref: '#/components/schemas/EventTypeEnum'
readOnly: true
description: 'The event type this webhook is subscribed to.
* `connection.expiry_scheduled` - Expiry Scheduled'
callback_url:
type: string
format: uri
readOnly: true
description: HTTPS URL that signed events are POSTed to.
is_active:
type: boolean
readOnly: true
description: Whether the webhook is active. Inactive webhooks do not receive
events.
created_at:
type: string
format: date-time
readOnly: true
description: ISO 8601 timestamp when the webhook was created.
updated_at:
type: string
format: date-time
readOnly: true
description: ISO 8601 timestamp when the webhook was last updated.
secret:
type: string
readOnly: true
description: Standard Webhooks signing secret (whsec_…). Shown once, at
creation.
required:
- callback_url
- created_at
- event_type
- id
- is_active
- secret
- updated_at
ErrorDetail:
type: object
description: 'Per the schema defined in the engineering index.
https://engineering.zapier.com/guides/api-design-guidelines/error-handling/'
properties:
code:
type: string
description: A unique identifier for this particular occurrence of the problem.
detail:
type: string
description: A human-readable explanation specific to this occurrence of
the problem.
status:
type: integer
description: HTTP status code for that error.
title:
type: string
description: A short summary of the problem.
source:
type: object
additionalProperties: {}
description: An object containing references to the primary source of the
error.
required:
- code
- detail
Errors:
type: object
properties:
errors:
type: array
items:
$ref: '#/components/schemas/ErrorDetail'
description: An array of error objects.
required:
- errors
EventTypeEnum:
enum:
- connection.expiry_scheduled
type: string
description: '* `connection.expiry_scheduled` - Expiry Scheduled'
PaginatedConnectionWebhookList:
type: object
required:
- links
- meta
- results
properties:
results:
description: Connection webhooks for the current page.
type: array
items:
$ref: '#/components/schemas/ConnectionWebhook'
meta:
type: object
description: Limit-offset pagination metadata.
required:
- count
- limit
- offset
properties:
limit:
type: integer
description: Number of results returned per page.
offset:
type: integer
description: Starting index for this page of results.
count:
type: integer
description: Total number of results across all pages.
links:
type: object
description: Pagination navigation links.
required:
- next
properties:
next:
type:
- string
- 'null'
format: uri
description: Link to the next page of results; null when on the last
page.
PatchedConnectionWebhookUpdate:
type: object
description: Input serializer for PATCH /webhooks/{id}. At least one field required.
properties:
callback_url:
type: string
format: uri
description: New HTTPS URL for event delivery.
is_active:
type: boolean
description: Pause (false) or resume (true) delivery.
TypeEnum:
enum:
- connection.expiry_scheduled
type: string
description: '* `connection.expiry_scheduled` - Expiry Scheduled'
securitySchemes:
OAuth:
type: oauth2
description: OAuth 2.0 authentication.
flows:
authorizationCode:
authorizationUrl: https://zapier.com/oauth/authorize
tokenUrl: https://zapier.com/oauth/token
scopes:
connection:webhook:read: Grants connection:webhook:read access
external: Scope that gives access to all Public APIs
connection:webhook:write: Grants connection:webhook:write access
clientCredentials:
tokenUrl: https://zapier.com/oauth/token
scopes:
connection:webhook:read: Grants connection:webhook:read access
external: Scope that gives access to all Public APIs
connection:webhook:write: Grants connection:webhook:write access

View file

@ -0,0 +1,505 @@
openapi: 3.1.0
info:
title: Powered by Zapier Promotions API
version: promotions
description: The API to define promotions, powered by Zapier. See https://docs.zapier.com/powered-by-zapier/introduction for more information.
contact:
name: Partner Sharing
paths:
/v2/promotions:
post:
operationId: root_create
description: |-
Enrolls an account into an existing promotion.
Endpoint available to Partners only.
The request must be authenticated by an [access token](https://docs.zapier.com/powered-by-zapier/api-reference/authentication#retrieving-a-user-access-token)
that the user has granted to the Partner for the account.
The `enrollment_id`, returned after successfully enrolling a user,
**must** be stored by the partner in order to [retrieve](https://docs.zapier.com/powered-by-zapier/api-reference/promotions/get-enrollment)
or [delete](https://docs.zapier.com/powered-by-zapier/api-reference/promotions/delete-enrollment) the enrollment.
#### OAuth
This endpoint requires the `promotions:write` OAuth scope.
summary: Create a promotion enrollment
tags:
- Promotions
- Experimental
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionEnrollment'
examples:
EnrollInAPromotion:
value:
promotion_id: promo_12345
summary: Enroll in a promotion
required: true
security:
- OAuth:
- promotions:write
responses:
'201':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionEnrollmentResponse'
description: Successful Enrollment
'404':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
examples:
'404':
value:
errors:
- status: '404'
code: promotion_not_found
title: Promotion Not Found
detail: The requested promotion was not found. The specified promotion_id may be invalid.
description: 404 Not Found
'409':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
examples:
Conflict:
value:
errors:
- status: '409'
code: already_enrolled
title: Already Enrolled
detail: The provided account is already enrolled in the specified promotion.
description: Conflict - User is already enrolled in this promotion
'429':
headers:
Retry-After:
schema:
type: string
format: uri
description: Indicates when to retry the request
X-RateLimit-Limit:
schema:
type: string
format: uri
description: The maximum number of requests you're permitted to make per hour.
X-RateLimit-Remaining:
schema:
type: string
format: uri
description: The number of requests remaining in the current rate limit window.
X-RateLimit-Reset:
schema:
type: string
format: uri
description: The time at which the current rate limit window resets in UTC epoch seconds.
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
description: ''
'503':
headers:
Retry-After:
schema:
type: string
format: uri
description: Indicates when to retry the request
X-RateLimit-Limit:
schema:
type: string
format: uri
description: The maximum number of requests you're permitted to make per hour.
X-RateLimit-Remaining:
schema:
type: string
format: uri
description: The number of requests remaining in the current rate limit window.
X-RateLimit-Reset:
schema:
type: string
format: uri
description: The time at which the current rate limit window resets in UTC epoch seconds.
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
examples:
Forbidden:
value:
errors:
- status: '403'
code: permission_denied
title: Permission Denied
detail: You do not have permission to perform this action.
description: Forbidden - User lacks permission to access this location
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
examples:
Access:
value:
errors:
- status: '401'
code: not_authenticated
title: User Not Authenticated
detail: User must be authenticated to access this resource. No valid user access token was provided.
description: 'Access denied: User does not have permission for enrollment_id: enroll_67890'
/v2/promotions/{enrollment_id}:
get:
operationId: root_retrieve
description: |-
Retrieve promotion enrollment details by enrollment ID.
Endpoint available to Partners only.
The request must be authenticated by a user access token
that the user has granted to the Partner for the account.
#### OAuth
This endpoint requires the `promotions:read` OAuth scope.
parameters:
- in: path
name: enrollment_id
schema:
type: string
required: true
tags:
- Promotions
- Experimental
security:
- OAuth:
- promotions:read
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionDetail'
examples:
GetPromotionDetails:
value:
promotion_id: test_promo
enrollment_date: '2026-04-28T00:00:00Z'
expiration_date: '2026-07-26T23:59:59Z'
task_limit_per_month: 5
tasks_used_this_month: 0
status: enrolled
summary: Get Promotion Details
description: Succcessful retrieval of promotion details
'404':
description: No response body
'429':
headers:
Retry-After:
schema:
type: string
format: uri
description: Indicates when to retry the request
X-RateLimit-Limit:
schema:
type: string
format: uri
description: The maximum number of requests you're permitted to make per hour.
X-RateLimit-Remaining:
schema:
type: string
format: uri
description: The number of requests remaining in the current rate limit window.
X-RateLimit-Reset:
schema:
type: string
format: uri
description: The time at which the current rate limit window resets in UTC epoch seconds.
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
description: ''
'503':
headers:
Retry-After:
schema:
type: string
format: uri
description: Indicates when to retry the request
X-RateLimit-Limit:
schema:
type: string
format: uri
description: The maximum number of requests you're permitted to make per hour.
X-RateLimit-Remaining:
schema:
type: string
format: uri
description: The number of requests remaining in the current rate limit window.
X-RateLimit-Reset:
schema:
type: string
format: uri
description: The time at which the current rate limit window resets in UTC epoch seconds.
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
examples:
Forbidden:
value:
errors:
- status: '403'
code: permission_denied
title: Permission Denied
detail: You do not have permission to perform this action.
description: Forbidden - User lacks permission to access this location
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
examples:
Access:
value:
errors:
- status: '401'
code: not_authenticated
title: User Not Authenticated
detail: User must be authenticated to access this resource. No valid user access token was provided.
description: 'Access denied: User does not have permission for enrollment_id: enroll_67890'
delete:
operationId: root_destroy
description: |-
Unenroll an account from a promotion.
Endpoint available to Partners only.
The request must be authenticated by an app access token the
Partner has received using the Client Credentials flow.
#### OAuth
This endpoint requires the `promotions:write` OAuth scope.
parameters:
- in: path
name: enrollment_id
schema:
type: string
required: true
tags:
- Promotions
- Experimental
security:
- OAuth:
- promotions:write
responses:
'204':
description: No response body
'429':
headers:
Retry-After:
schema:
type: string
format: uri
description: Indicates when to retry the request
X-RateLimit-Limit:
schema:
type: string
format: uri
description: The maximum number of requests you're permitted to make per hour.
X-RateLimit-Remaining:
schema:
type: string
format: uri
description: The number of requests remaining in the current rate limit window.
X-RateLimit-Reset:
schema:
type: string
format: uri
description: The time at which the current rate limit window resets in UTC epoch seconds.
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
description: ''
'503':
headers:
Retry-After:
schema:
type: string
format: uri
description: Indicates when to retry the request
X-RateLimit-Limit:
schema:
type: string
format: uri
description: The maximum number of requests you're permitted to make per hour.
X-RateLimit-Remaining:
schema:
type: string
format: uri
description: The number of requests remaining in the current rate limit window.
X-RateLimit-Reset:
schema:
type: string
format: uri
description: The time at which the current rate limit window resets in UTC epoch seconds.
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
description: ''
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
examples:
Forbidden:
value:
errors:
- status: '403'
code: permission_denied
title: Permission Denied
detail: You do not have permission to perform this action.
description: Forbidden - User lacks permission to access this location
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/PromotionErrorResponse'
examples:
Access:
value:
errors:
- status: '401'
code: not_authenticated
title: User Not Authenticated
detail: User must be authenticated to access this resource. No valid user access token was provided.
description: 'Access denied: User does not have permission for enrollment_id: enroll_67890'
components:
schemas:
PromotionDetail:
type: object
description: Succcessful retrieval of promotion details
properties:
promotion_id:
type: string
description: The unique identifier for the promotion
enrollment_date:
type:
- string
- 'null'
format: date-time
description: Date and time of enrollment
expiration_date:
type:
- string
- 'null'
format: date-time
description: Date and time when the promotion expires
task_limit_per_month:
type:
- integer
- 'null'
description: Maximum number of tasks allowed per monthly billing cycle
tasks_used_this_month:
type: integer
description: Number of tasks already used this monthly billing cycle (may not align with calendar month)
status:
type: string
description: Current status of the enrollment
required:
- enrollment_date
- expiration_date
- promotion_id
- status
- task_limit_per_month
- tasks_used_this_month
PromotionEnrollment:
type: object
properties:
promotion_id:
type: string
description: The unique identifier for the promotions
required:
- promotion_id
PromotionEnrollmentResponse:
type: object
description: Successful Enrollment
properties:
enrollment_id:
type: string
description: The unique identifier for the enrollment. This must be stored by partners.
required:
- enrollment_id
PromotionError:
type: object
properties:
status:
type: string
description: HTTP status code of the error
code:
type: string
description: Machine-readable error code
title:
type: string
description: Human-readable error title
detail:
type: string
description: Detailed description of the error
required:
- code
- detail
- status
- title
PromotionErrorResponse:
type: object
properties:
errors:
type: array
items:
$ref: '#/components/schemas/PromotionError'
description: Errors encountered processing the request
required:
- errors
securitySchemes:
OAuth:
type: oauth2
description: The user access token for the user you would like to enroll. See our [OAuth2 authentication documentation](https://docs.zapier.com/powered-by-zapier/api-reference/authentication#retrieving-a-user-access-token).
flows:
authorizationCode:
authorizationUrl: https://zapier.com/oauth/authorize/
tokenUrl: https://zapier.com/oauth/token/
refreshUrl: https://zapier.com/oauth/token/
scopes:
promotions:write: Enroll or unenroll accounts into promotions
promotions:read: View usage of promotions you manage on an account
clientCredentials:
tokenUrl: /oauth/token
scopes:
promotions:write: Unenroll accounts from promotions
servers:
- url: https://api.zapier.com
description: Production
tags:
- name: Promotions
description: Operations related to managing Promotions
- name: Experimental
description: Operations that are not to be considered finalized, and are subject to change

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff