Every API here is available over the APIs.io API and to AI agents over MCP.
MCP server
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
Tools for apis
7 MCP tools reach this
find_apisBrowse and filter every API in the catalog.
get_api_artifactsOne API's artifacts, grouped by type.
get_openapiThe primary OpenAPI for this API.
find_similar_apisAPIs that look like this one.
apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
resolveTurn a domain, URL or GitHub org into the provider it belongs to.
find_cohortsEvery scored population of providers in the catalog.
All 92 tools →
Call it yourself
curl for this page
This API
curl "https://apis.io/api/v1/apis/growsurf-com:growsurf-com-campaign-webhooks-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: GrowSurf REST Campaign Webhooks API
version: 2.0.0
description: 'Public GrowSurf REST API for managing programs, participants, referrals,
rewards, affiliate commissions, payouts, and analytics.
Every endpoint requires a GrowSurf REST API key in the `Authorization: Bearer `
header. To get one, sign up at https://app.growsurf.com, then create an API key in the GrowSurf
dashboard. You can create multiple API keys, with optional scopes and program access limits.
Each operation lists its required API-key scope in `x-growsurf-required-scopes`.
Team scopes cover team details and verification; program scopes cover program settings,
reward configuration, and webhooks; participant scopes cover participant records and
referrals; reward scopes cover sales, commissions, payouts, and issued rewards; and
analytics scope covers aggregate reporting. `api_key:rotate` is available only to direct
API-key REST/SDK clients and is unavailable through MCP. `reward:fulfill` is separate
because fulfillment may deliver something of value.
The team owner''s email address must be verified before the API can manage programs and their
resources. Until it is, those endpoints return a `403` with error code
`EMAIL_NOT_VERIFIED_ERROR`. Resend the owner verification email via
`POST /team/owner/verification-email`, then retry.
For agent-led onboarding, use the canonical Arazzo 1.1 workflow at
`https://growsurf.com/arazzo.yaml`. Starting from an API key the account owner created, it
configures and verifies a draft program. It never launches a program or performs a paid,
reward, payout, key-rotation, or destructive action.
REST responses include `RateLimit-Policy` and `RateLimit` fields so clients can throttle before
they reach a quota. Before authentication, these fields describe only the caller''s public
network budget. After authentication, they describe the credential and team budgets. A `429`
response also includes `Retry-After`; that value takes precedence when it is present. GrowSurf''s
legacy rate-limit fields remain available for backward compatibility.
GrowSurf versions this API in the URL and gives at least 12 calendar months of public notice
before a planned breaking removal. The notice period starts when the public OpenAPI schema marks
the affected surface as deprecated and GrowSurf publishes migration guidance. Urgent security,
privacy, legal, or service-integrity requirements can require a shorter window. See the public
lifecycle policy for the full rules and machine-readable signals.
Every REST response carries `Link: ; rel="deprecation"` (RFC 9745), which points at
that lifecycle policy. A surface that is actually deprecated adds a `Deprecation` field, and one
with a planned shutdown date adds a `Sunset` field (RFC 8594). No `Deprecation` field means the
surface is not deprecated.'
contact:
name: GrowSurf Support
url: https://growsurf.com/settings#contact_support
license:
name: Proprietary
url: https://growsurf.com/terms
x-deprecation-policy:
url: https://docs.growsurf.com/developer-tools/rest-api/api-lifecycle
versioning: URL path (/v2)
minimumNoticeMonths: 12
signals:
- 'Link: <https://docs.growsurf.com/developer-tools/rest-api/api-lifecycle>; rel="deprecation" (RFC 9745) on every response'
- Deprecation response field (RFC 9745) once a surface is deprecated
- Sunset response field (RFC 8594) once a shutdown date is planned
- 'deprecated: true in this OpenAPI document'
servers:
- url: https://api.growsurf.com/v2
description: Production
security:
- apiKeyAuth: []
tags:
- name: Campaign Webhooks
description: Program webhook configuration (create, update, delete, and test webhooks).
paths:
/campaign/{id}/webhooks:
get:
tags:
- Campaign Webhooks
operationId: listWebhooks
x-growsurf-required-scopes:
- program:read
summary: List webhooks
description: Lists a program's webhooks (secrets are never returned).
parameters:
- $ref: '#/components/parameters/CampaignId'
responses:
'200':
description: Webhooks returned.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookListResponse'
examples:
default:
$ref: '#/components/examples/WebhookListExample'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
post:
tags:
- Campaign Webhooks
operationId: createWebhook
x-growsurf-required-scopes:
- program:write
summary: Create webhook
description: 'Adds a webhook to the program. A program can have only one saved Bask Health webhook across all `bask.ninja` hosts and paths, including disabled webhooks. A second Bask Health webhook returns `400`.
A concurrent change to webhook settings can return `409`; reload the webhooks before retrying.'
parameters:
- $ref: '#/components/parameters/CampaignId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateWebhookRequest'
examples:
default:
$ref: '#/components/examples/CreateWebhookExample'
responses:
'200':
description: Webhook created.
content:
application/json:
schema:
$ref: '#/components/schemas/Webhook'
examples:
default:
$ref: '#/components/examples/WebhookExample'
'400':
$ref: '#/components/responses/BadRequest'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/RateLimited'
/campaign/{id}/webhooks/{webhookId}:
patch:
tags:
- Campaign Webhooks
operationId: updateWebhook
x-growsurf-required-scopes:
- program:write
summary: Update webhook
description: 'Updates a webhook by id. A program can have only one saved Bask Health webhook across all `bask.ninja` hosts and paths, including disabled webhooks. An update that leaves more than one Bask Health webhook returns `400`.
A concurrent change to webhook settings can return `409`; reload the webhooks before retrying.'
parameters:
- $ref: '#/components/parameters/CampaignId'
- $ref: '#/components/parameters/WebhookId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateWebhookRequest'
examples:
default:
$ref: '#/components/examples/UpdateWebhookExample'
responses:
'200':
description: Webhook updated.
content:
application/json:
schema:
$ref: '#/components/schemas/Webhook'
examples:
default:
$ref: '#/components/examples/WebhookExample'
'400':
$ref: '#/components/responses/BadRequest'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/Conflict'
'429':
$ref: '#/components/responses/RateLimited'
delete:
tags:
- Campaign Webhooks
operationId: deleteWebhook
x-growsurf-required-scopes:
- program:write
summary: Delete webhook
description: Removes a webhook by id.
parameters:
- $ref: '#/components/parameters/CampaignId'
- $ref: '#/components/parameters/WebhookId'
responses:
'200':
description: Webhook deleted.
content:
application/json:
schema:
$ref: '#/components/schemas/DeleteWebhookResponse'
examples:
default:
$ref: '#/components/examples/DeleteWebhookExample'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
/campaign/{id}/webhooks/{webhookId}/test:
post:
tags:
- Campaign Webhooks
operationId: testWebhook
x-growsurf-required-scopes:
- program:write
summary: Test webhook
description: Sends a live test event to a webhook using its stored URL and secret.
parameters:
- $ref: '#/components/parameters/CampaignId'
- $ref: '#/components/parameters/WebhookId'
requestBody:
required: false
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookTestRequest'
examples:
default:
$ref: '#/components/examples/WebhookTestExample'
responses:
'200':
description: Test result.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookTestResponse'
'400':
$ref: '#/components/responses/BadRequest'
'402':
$ref: '#/components/responses/PaymentRequired'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'429':
$ref: '#/components/responses/RateLimited'
components:
responses:
Conflict:
description: Conflicting duplicate request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
PaymentRequired:
description: The account's billing requirements for this action are not met.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
NotFound:
description: Resource not found.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
RateLimited:
description: Too many requests.
headers:
RateLimit-Policy:
description: Named quota policies, quotas, and windows using the IETF RateLimit Structured Fields format.
schema:
type: string
example: '"second";q=50;w=5, "minute";q=400;w=60, "hour";q=20000;w=3600'
RateLimit:
description: Remaining quota and reset time in seconds for the active policy or policies.
schema:
type: string
example: '"second";r=0;t=30'
Retry-After:
description: Whole seconds to wait before retrying this request. This field takes precedence over RateLimit.
schema:
type: string
pattern: ^[0-9]+$
example: '30'
GrowSurf-RateLimit-Second-Limit:
description: Number of API requests allowed per second policy.
schema:
type: integer
GrowSurf-RateLimit-Second-Remaining:
description: Number of API requests remaining in the second policy.
schema:
type: integer
GrowSurf-Retry-After-Second-Milliseconds:
description: Milliseconds until the second policy window resets.
schema:
type: integer
GrowSurf-RateLimit-Minute-Limit:
description: Number of API requests allowed per minute policy.
schema:
type: integer
GrowSurf-RateLimit-Minute-Remaining:
description: Number of API requests remaining in the minute policy.
schema:
type: integer
GrowSurf-Retry-After-Minute-Milliseconds:
description: Milliseconds until the minute policy window resets.
schema:
type: integer
GrowSurf-RateLimit-Hour-Limit:
description: Number of API requests allowed per hour policy.
schema:
type: integer
GrowSurf-RateLimit-Hour-Remaining:
description: Number of API requests remaining in the hour policy.
schema:
type: integer
GrowSurf-Retry-After-Hour-Milliseconds:
description: Milliseconds until the hour policy window resets.
schema:
type: integer
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
Forbidden:
description: The API key is missing, invalid, revoked, outside its scope, or not allowed to perform this action.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
emailNotVerified:
summary: Account owner email verification is required
value:
name: EmailNotVerifiedError
code: EMAIL_NOT_VERIFIED_ERROR
message: The team owner email address must be verified to use this endpoint. Check the owner inbox, or resend it via POST /v2/team/owner/verification-email. Retry this request with the same API key once verified.
status: 403
missingCredential:
summary: No API key reached the API
value:
name: NotAuthorizedError
code: NOT_AUTHORIZED_ERROR
message: 'No API key provided. Send it as an Authorization: Bearer header.'
status: 403
invalidCredential:
summary: The API key was rejected
value:
name: NotAuthorizedError
code: NOT_AUTHORIZED_ERROR
message: The API key provided is not valid. It may have been rotated or revoked.
status: 403
notAuthorized:
summary: The credential cannot perform this action
value:
name: NotAuthorizedError
code: NOT_AUTHORIZED_ERROR
message: You are not authorized to perform this action.
status: 403
BadRequest:
description: Invalid request.
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
examples:
DeleteWebhookExample:
value:
id: primary
success: true
WebhookTestExample:
value:
event: NEW_PARTICIPANT_ADDED
WebhookExample:
value:
id: primary
payloadUrl: https://piedpiper.com/growsurf/webhook
events:
- NEW_PARTICIPANT_ADDED
- PARTICIPANT_REACHED_A_GOAL
isEnabled: true
autoDisabledDueToFailures: false
failureCount: 0
lastFailureAt: null
UpdateWebhookExample:
value:
events:
- NEW_PARTICIPANT_ADDED
isEnabled: false
CreateWebhookExample:
value:
payloadUrl: https://piedpiper.com/growsurf/webhook
events:
- NEW_PARTICIPANT_ADDED
- PARTICIPANT_REACHED_A_GOAL
secret: whsec_middleout
isEnabled: true
WebhookListExample:
value:
webhooks:
- id: primary
payloadUrl: https://piedpiper.com/growsurf/webhook
events:
- NEW_PARTICIPANT_ADDED
- PARTICIPANT_REACHED_A_GOAL
isEnabled: true
autoDisabledDueToFailures: false
failureCount: 0
lastFailureAt: null
schemas:
Error:
type: object
properties:
name:
description: Error name returned for the failed request.
type: string
example: BadRequestError
code:
description: Stable machine-readable error code.
type: string
example: BAD_REQUEST_ERROR
message:
description: Human-readable error message.
type: string
example: Invalid request. Request params are missing or are invalid
errors:
description: Optional validation details for specific request fields.
type: array
items:
type: object
additionalProperties: true
example:
- field: email
message: Invalid email address.
status:
description: HTTP status code for the error.
type: integer
example: 400
supportUrl:
description: Support URL related to the error, when available.
type: string
format: uri
example: https://app.growsurf.com/settings#contact_support
policyName:
description: Rate-limit policy name for `429` errors, when available.
type: string
example: minute
level:
description: Error severity level, when available.
type: string
example: error
timestamp:
description: When the error response was created.
type: string
format: date-time
example: '2026-07-08T12:00:00.000Z'
example:
name: BadRequestError
code: BAD_REQUEST_ERROR
message: Invalid request. Request params are missing or are invalid
status: 400
supportUrl: https://app.growsurf.com/settings#contact_support
CreateWebhookRequest:
type: object
description: A program can have up to 5 webhooks, counting the primary webhook slot, so you can create up to 4. Creating another returns `400`.
required:
- payloadUrl
additionalProperties: false
properties:
payloadUrl:
type: string
description: The URL that receives webhook deliveries.
example: https://piedpiper.com/growsurf/webhook
events:
type: array
items:
$ref: '#/components/schemas/WebhookEvent'
description: 'The events this webhook is subscribed to. When omitted, the webhook is subscribed to every event its program type offers, like a webhook added in the dashboard: referral programs offer `PARTICIPANT_REACHED_A_GOAL`, `NEW_PARTICIPANT_ADDED`, `PARTICIPANT_FRAUD_STATUS_UPDATED`, and `CAMPAIGN_ENDED`; affiliate programs offer `NEW_COMMISSION_ADDED`, `COMMISSION_ADJUSTED`, `NEW_PAYOUT_ISSUED`, `NEW_PARTICIPANT_ADDED`, `PARTICIPANT_FRAUD_STATUS_UPDATED`, and `CAMPAIGN_ENDED`. Send `[]` to subscribe to no events. An event the program type does not offer returns `400`.'
example:
- NEW_PARTICIPANT_ADDED
- PARTICIPANT_REACHED_A_GOAL
secret:
type: string
writeOnly: true
description: Write-only. Used to sign deliveries (the `GrowSurf-Signature` HMAC header). Never returned.
example: whsec_piedpiper_webhook_secret
isEnabled:
description: Whether the webhook should start enabled. Defaults to `true`.
type: boolean
default: true
UpdateWebhookRequest:
type: object
additionalProperties: false
properties:
payloadUrl:
description: New URL that receives webhook deliveries.
type: string
example: https://piedpiper.com/growsurf/webhook
events:
description: Replacement event subscription list for this webhook. Events must be ones the program type offers (see `CreateWebhookRequest.events`); an event this webhook already has on is also accepted.
type: array
items:
$ref: '#/components/schemas/WebhookEvent'
example:
- NEW_PARTICIPANT_ADDED
- NEW_COMMISSION_ADDED
secret:
type: string
writeOnly: true
description: Write-only.
example: whsec_rotated_piedpiper_secret
isEnabled:
description: Whether deliveries to this webhook are enabled.
type: boolean
WebhookListResponse:
type: object
required:
- webhooks
properties:
webhooks:
description: Webhooks configured for the program.
type: array
items:
$ref: '#/components/schemas/Webhook'
WebhookTestResponse:
type: object
required:
- success
properties:
success:
description: Whether the test webhook request completed.
type: boolean
payload:
type: object
additionalProperties: true
description: The mock event payload that was sent.
example:
event: NEW_PARTICIPANT_ADDED
participant:
id: f8g9nl
email: gavin@hooli.com
response:
description: Response returned by the webhook endpoint during the test.
type: object
properties:
msg:
description: Response message returned by the webhook endpoint.
type: string
example: OK
statusCode:
description: HTTP status code returned by the webhook endpoint.
type: integer
example: 200
WebhookTestRequest:
type: object
additionalProperties: false
properties:
event:
$ref: '#/components/schemas/WebhookEvent'
description: The event to simulate. When omitted, the webhook's first enabled event is used, and the request returns `400` if the webhook has no enabled events.
Webhook:
type: object
description: A program webhook's configuration (payload URL, subscribed events, enabled state) plus read-only delivery-health fields.
required:
- id
- events
- isEnabled
- autoDisabledDueToFailures
- failureCount
properties:
id:
type: string
readOnly: true
description: The webhook id (`primary` for the program's primary webhook).
example: primary
payloadUrl:
type:
- string
- 'null'
description: The URL that receives webhook deliveries.
example: https://piedpiper.com/growsurf/webhook
events:
description: Webhook events this endpoint is subscribed to.
type: array
items:
$ref: '#/components/schemas/WebhookEvent'
example:
- NEW_PARTICIPANT_ADDED
- PARTICIPANT_REACHED_A_GOAL
isEnabled:
description: Whether deliveries to this webhook are enabled.
type: boolean
example: true
autoDisabledDueToFailures:
type: boolean
readOnly: true
description: Read-only. Whether GrowSurf auto-disabled this webhook after repeated delivery failures.
example: false
failureCount:
type: integer
readOnly: true
description: Read-only. Consecutive delivery failures.
example: 0
lastFailureAt:
type:
- integer
- 'null'
format: int64
readOnly: true
description: Read-only. When the last delivery failure occurred, as a Unix timestamp in milliseconds.
example: null
DeleteWebhookResponse:
type: object
required:
- id
- success
properties:
id:
description: ID of the webhook that was deleted.
type: string
success:
description: Whether the webhook was deleted.
type: boolean
WebhookEvent:
type: string
description: A webhook event name (the events GrowSurf delivers to subscribed webhooks).
enum:
- PARTICIPANT_REACHED_A_GOAL
- NEW_PARTICIPANT_ADDED
- CAMPAIGN_ENDED
- PARTICIPANT_FRAUD_STATUS_UPDATED
- NEW_COMMISSION_ADDED
- COMMISSION_ADJUSTED
- NEW_PAYOUT_ISSUED
example: NEW_PARTICIPANT_ADDED
parameters:
WebhookId:
name: webhookId
in: path
required: true
description: Webhook ID (`primary` for the program's primary webhook, or the id of an additional webhook).
schema:
type: string
CampaignId:
name: id
in: path
required: true
description: GrowSurf program ID.
schema:
type: string
securitySchemes:
apiKeyAuth:
type: http
scheme: bearer
bearerFormat: API key
description: 'GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`.'
externalDocs:
description: GrowSurf REST API lifecycle, versioning, and deprecation policy
url: https://docs.growsurf.com/developer-tools/rest-api/api-lifecycle