GrowSurf Campaign Webhooks API

Program webhook configuration (create, update, delete, and test webhooks).

GitHub OpenAPI

Operations 5

GET /campaign/{id}/webhooks List webhooks #
POST /campaign/{id}/webhooks Create webhook #
PATCH /campaign/{id}/webhooks/{webhookId} Update webhook #
DELETE /campaign/{id}/webhooks/{webhookId} Delete webhook #
POST /campaign/{id}/webhooks/{webhookId}/test Test webhook #

Work with this as data

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 Specification

growsurf-com-campaign-webhooks-api-openapi.yml Raw ↑
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