> For the complete documentation index, see [llms.txt](https://docs.growsurf.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.growsurf.com/developer-tools/rest-api/api-reference.md).

# API Reference

{% hint style="info" %}

* **Using AI?** If you're using an AI tool such as Cursor, Codex, Claude Code, or Antigravity to help you implement GrowSurf, we recommend utilizing our [MCP server](https://docs.growsurf.com/build-with-ai).
* **Using TypeScript, Python, PHP, Ruby, or Java?** Use an official GrowSurf API library. [Learn more](https://docs.growsurf.com/developer-tools/rest-api/api-libraries).
* **Building a native iOS or Android app?** Use the [iOS SDK](/developer-tools/ios-sdk.md) or [Android SDK](/developer-tools/android-sdk.md) for mobile attribution, participant creation, sharing, and referral portal data. Use this REST API from your backend for secure server-side actions.
  {% endhint %}

{% hint style="warning" %}
Do not embed your REST API key in native mobile apps. The iOS and Android SDKs use a separate Mobile SDK API and public Mobile SDK key. This REST API remains the right place for backend-only operations, including server-verified purchase or subscription referral qualification.
{% endhint %}

## INDEX ↓

| Section                                                                                                         | Endpoints                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| --------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Accounts](https://docs.growsurf.com/developer-tools/rest-api/api-reference#accounts)                           | [Create account](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-accounts)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| [Teams](https://docs.growsurf.com/developer-tools/rest-api/api-reference#teams)                                 | [Retrieve team](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-team) · [Update team](https://docs.growsurf.com/developer-tools/rest-api/api-reference#patch-team) · [Request team verification](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-team-verification-request) · [Resend team owner verification email](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-team-owner-verification-email) · [Rotate API key](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-api-key-rotate)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| [Campaigns](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaigns)                         | [List campaigns](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaigns) · [Retrieve a campaign](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id) · [Create a campaign](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaigns) · [Update a campaign](https://docs.growsurf.com/developer-tools/rest-api/api-reference#patch-campaign-id) · [Clone a campaign](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-clone)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [Campaign Rewards](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-rewards)           | [List campaign rewards](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-reward-configs) · [Create a campaign reward](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-reward-configs) · [Update a campaign reward](https://docs.growsurf.com/developer-tools/rest-api/api-reference#patch-campaign-id-reward-configs-campaignrewardid) · [Delete a campaign reward](https://docs.growsurf.com/developer-tools/rest-api/api-reference#delete-campaign-id-reward-configs-campaignrewardid)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| [Campaign Design](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-design)             | [Retrieve campaign design](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-design) · [Update campaign design](https://docs.growsurf.com/developer-tools/rest-api/api-reference#patch-campaign-id-design)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [Campaign Emails](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-emails)             | [Retrieve campaign emails](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-emails) · [Update campaign emails](https://docs.growsurf.com/developer-tools/rest-api/api-reference#patch-campaign-id-emails)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [Campaign Options](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-options)           | [Retrieve campaign options](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-options) · [Update campaign options](https://docs.growsurf.com/developer-tools/rest-api/api-reference#patch-campaign-id-options)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| [Campaign Installation](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-installation) | [Retrieve campaign installation](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-installation) · [Update campaign installation](https://docs.growsurf.com/developer-tools/rest-api/api-reference#patch-campaign-id-installation)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| [Campaign Webhooks](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-webhooks)         | [List webhooks](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-webhooks) · [Create webhook](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-webhooks) · [Update webhook](https://docs.growsurf.com/developer-tools/rest-api/api-reference#patch-campaign-id-webhooks-webhookid) · [Delete webhook](https://docs.growsurf.com/developer-tools/rest-api/api-reference#delete-campaign-id-webhooks-webhookid) · [Test webhook](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-webhooks-webhookid-test)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| [Participants](https://docs.growsurf.com/developer-tools/rest-api/api-reference#participants)                   | [List participants](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-participants) · [List leaderboard participants](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-leaderboard) · [Add participant](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-participant) · [Retrieve participant](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-participant-participantidoremail) · [Update participant](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-participant-participantidoremail) · [Delete participant](https://docs.growsurf.com/developer-tools/rest-api/api-reference#delete-campaign-id-participant-participantidoremail) · [Bulk delete participants](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-participants-bulk-delete) · [Trigger referral](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-participant-participantidoremail-ref) · [Cancel delayed referral trigger](https://docs.growsurf.com/developer-tools/rest-api/api-reference#delete-campaign-id-participant-participantidoremail-ref) · [Create mobile participant token](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-mobile-participant-token) · [Email participant](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-participant-participantidoremail-email) · [Retrieve participant analytics](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-participant-participantidoremail-analytics) · [List participant activity logs](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-participant-participantidoremail-activity-logs) |
| [Participant Rewards](https://docs.growsurf.com/developer-tools/rest-api/api-reference#participant-rewards)     | [List participant rewards](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-participant-participantidoremail-rewards) · [Approve participant reward](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-reward-rewardid-approve) · [Fulfill participant reward](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-reward-rewardid-fulfill) · [Delete participant reward](https://docs.growsurf.com/developer-tools/rest-api/api-reference#delete-campaign-id-reward-rewardid)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| [Referrals and Invites](https://docs.growsurf.com/developer-tools/rest-api/api-reference#referrals-and-invites) | [List referrals and invites](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-referrals) · [List participant referrals and invites](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-participant-participantidoremail-referrals) · [Send participant invites](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-participant-participantidoremail-invites)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [Analytics](https://docs.growsurf.com/developer-tools/rest-api/api-reference#analytics)                         | [Retrieve campaign analytics](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-analytics)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| [Affiliate Programs](https://docs.growsurf.com/developer-tools/rest-api/api-reference#affiliate-programs)       | [Record affiliate transaction](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-participant-participantidoremail-transaction) · [Refund or amend affiliate transaction](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-participant-participantidoremail-transaction-refund) · [List participant commissions](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-commissions) · [List commissions for a participant](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-participant-participantidoremail-commissions) · [Approve participant commission](https://docs.growsurf.com/developer-tools/rest-api/api-reference#post-campaign-id-commission-commissionid-approve) · [Delete participant commission](https://docs.growsurf.com/developer-tools/rest-api/api-reference#delete-campaign-id-commission-commissionid) · [List participant payouts](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-payouts) · [List payouts for a participant](https://docs.growsurf.com/developer-tools/rest-api/api-reference#get-campaign-id-participant-participantidoremail-payouts)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |

***

## ACCOUNTS ↓

{% hint style="info" %}
Create a GrowSurf account and receive its first API key.
{% endhint %}

## Create account

> Creates a new GrowSurf account. This is the only endpoint that does not require an API key. The response includes an API key for the new account, shown once in the response. The key is locked until the team owner's email address is verified: authenticated program and resource endpoints return a \`403\` with error code \`EMAIL\_NOT\_VERIFIED\_ERROR\` until then (resend the email via \`POST /team/owner/verification-email\`, then retry). A welcome email is sent to the address with the verification link and a set-password link for dashboard access. Accounts whose email is never verified are deleted automatically after 7 days. For security, the API key is rotated the first time the account owner signs in to the GrowSurf dashboard. Some actions (such as emailing participants) additionally require GrowSurf to verify the team first. By creating an account you agree, on behalf of the account holder, to GrowSurf's \[Terms of Service]\(<https://growsurf.com/terms>) and \[Privacy Policy]\(<https://growsurf.com/privacy>).

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Accounts","description":"Create a GrowSurf account and its initial API key."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[],"paths":{"/accounts":{"post":{"tags":["Accounts"],"operationId":"createAccount","summary":"Create account","description":"Creates a new GrowSurf account. This is the only endpoint that does not require an API key. The response includes an API key for the new account, shown once in the response. The key is locked until the team owner's email address is verified: authenticated program and resource endpoints return a `403` with error code `EMAIL_NOT_VERIFIED_ERROR` until then (resend the email via `POST /team/owner/verification-email`, then retry). A welcome email is sent to the address with the verification link and a set-password link for dashboard access. Accounts whose email is never verified are deleted automatically after 7 days. For security, the API key is rotated the first time the account owner signs in to the GrowSurf dashboard. Some actions (such as emailing participants) additionally require GrowSurf to verify the team first. By creating an account you agree, on behalf of the account holder, to GrowSurf's [Terms of Service](https://growsurf.com/terms) and [Privacy Policy](https://growsurf.com/privacy).","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccountRequest"}}}},"responses":{"200":{"description":"Account created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAccountResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}}},"components":{"schemas":{"CreateAccountRequest":{"type":"object","required":["email"],"additionalProperties":false,"properties":{"email":{"type":"string","format":"email","description":"The email address for the new GrowSurf account. Personal emails and disposable email addresses are not accepted."},"firstName":{"description":"First name for the new account owner.","type":"string","maxLength":255},"lastName":{"description":"Last name for the new account owner.","type":"string","maxLength":255},"company":{"description":"Company name for the new account.","type":"string","maxLength":255}}},"CreateAccountResponse":{"type":"object","required":["email","apiKey","verificationStatus"],"properties":{"email":{"description":"Email address for the new account.","type":"string","format":"email"},"apiKey":{"type":"string","description":"An API key for the new account. Use it as the `Bearer` token on subsequent requests. It is shown once, locked (`403` `EMAIL_NOT_VERIFIED_ERROR`) until the account's email is verified, and rotated when the account owner first signs in to the GrowSurf dashboard."},"verificationStatus":{"type":"string","enum":["NOT_REQUESTED","REQUESTED","VERIFIED"],"description":"GrowSurf team verification state. `VERIFIED` is required before a program can send participant emails."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflicting duplicate request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}}}
```

***

## TEAMS ↓

{% hint style="info" %}
Read or update the team bound to your API key or OAuth connection, request GrowSurf verification, resend the team owner's verification email, or rotate the API key making a direct API request. New API keys and OAuth connections act on one team. API key rotation is not available through MCP.
{% endhint %}

## Retrieve team

> Retrieves the team bound to the API key or OAuth connection. \`verificationStatus\` is \`VERIFIED\` once GrowSurf has verified the team, which is required before a program can send participant emails.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Teams","description":"The team bound to an API key or OAuth connection."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"schemas":{"Team":{"type":"object","description":"The team bound to the API key or OAuth connection.","required":["name","verificationStatus","verificationRequestedAt"],"properties":{"name":{"description":"The team's display name.","type":"string"},"verificationStatus":{"type":"string","enum":["NOT_REQUESTED","REQUESTED","VERIFIED"],"readOnly":true,"description":"GrowSurf team verification state. `VERIFIED` is required before a program can send participant emails."},"verificationRequestedAt":{"type":"integer","format":"int64","nullable":true,"readOnly":true,"description":"When team verification was last requested, as a Unix timestamp in milliseconds."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"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"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/team":{"get":{"tags":["Teams"],"operationId":"getTeam","summary":"Retrieve team","description":"Retrieves the team bound to the API key or OAuth connection. `verificationStatus` is `VERIFIED` once GrowSurf has verified the team, which is required before a program can send participant emails.","responses":{"200":{"description":"Team returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update team

> Updates the name of the team bound to the API key or OAuth connection. Any other property is rejected with a \`400\`. Personal profiles, billing, and team ownership are not editable here.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Teams","description":"The team bound to an API key or OAuth connection."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"schemas":{"UpdateTeamRequest":{"type":"object","description":"The Team fields you can update. Any property not listed here is rejected with a `400`.","additionalProperties":false,"required":["name"],"properties":{"name":{"description":"The team's display name.","type":"string","minLength":1,"maxLength":255}}},"Team":{"type":"object","description":"The team bound to the API key or OAuth connection.","required":["name","verificationStatus","verificationRequestedAt"],"properties":{"name":{"description":"The team's display name.","type":"string"},"verificationStatus":{"type":"string","enum":["NOT_REQUESTED","REQUESTED","VERIFIED"],"readOnly":true,"description":"GrowSurf team verification state. `VERIFIED` is required before a program can send participant emails."},"verificationRequestedAt":{"type":"integer","format":"int64","nullable":true,"readOnly":true,"description":"When team verification was last requested, as a Unix timestamp in milliseconds."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/team":{"patch":{"tags":["Teams"],"operationId":"updateTeam","summary":"Update team","description":"Updates the name of the team bound to the API key or OAuth connection. Any other property is rejected with a `400`. Personal profiles, billing, and team ownership are not editable here.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTeamRequest"}}}},"responses":{"200":{"description":"Updated team returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Request team verification

> Requests GrowSurf to verify the bound team (required before a program can email its participants). Idempotent — calling it again while a request is pending does not create a duplicate. Returns the team with its updated \`verificationStatus\`.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Teams","description":"The team bound to an API key or OAuth connection."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"schemas":{"Team":{"type":"object","description":"The team bound to the API key or OAuth connection.","required":["name","verificationStatus","verificationRequestedAt"],"properties":{"name":{"description":"The team's display name.","type":"string"},"verificationStatus":{"type":"string","enum":["NOT_REQUESTED","REQUESTED","VERIFIED"],"readOnly":true,"description":"GrowSurf team verification state. `VERIFIED` is required before a program can send participant emails."},"verificationRequestedAt":{"type":"integer","format":"int64","nullable":true,"readOnly":true,"description":"When team verification was last requested, as a Unix timestamp in milliseconds."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"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"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/team/verification-request":{"post":{"tags":["Teams"],"operationId":"requestTeamVerification","summary":"Request team verification","description":"Requests GrowSurf to verify the bound team (required before a program can email its participants). Idempotent — calling it again while a request is pending does not create a duplicate. Returns the team with its updated `verificationStatus`.","responses":{"200":{"description":"Verification request recorded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Team"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Resend team owner verification email

> Resends the email-verification message to the bound team's owner. The response never reveals the owner's email address. A \`200\` with \`status: SENT\` is returned only when an email was actually dispatched. Returns \`400\` if the email is already verified, and \`429\` if a verification email was sent too recently — wait a moment, then retry.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Teams","description":"The team bound to an API key or OAuth connection."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"schemas":{"VerificationEmailResponse":{"type":"object","required":["success","status"],"properties":{"success":{"description":"Whether the verification email request was accepted.","type":"boolean"},"status":{"description":"Status of the verification email request.","type":"string","enum":["SENT"]}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/team/owner/verification-email":{"post":{"tags":["Teams"],"operationId":"resendTeamOwnerVerificationEmail","summary":"Resend team owner verification email","description":"Resends the email-verification message to the bound team's owner. The response never reveals the owner's email address. A `200` with `status: SENT` is returned only when an email was actually dispatched. Returns `400` if the email is already verified, and `429` if a verification email was sent too recently — wait a moment, then retry.","responses":{"200":{"description":"Verification email sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerificationEmailResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Rotate API key

> Generates a new API key and makes the key used on this request stop working when rotation succeeds. Send a unique, random \`Idempotency-Key\`. If the response is interrupted, immediately retry with the original API key and the same \`Idempotency-Key\` to receive the same new key. Update every integration that used the old key. The team owner is notified by email whenever the key is rotated. GrowSurf SDKs generate the idempotency key automatically. This endpoint accepts an API key with \`api\_key:rotate\`. If this scope is unavailable, rotate the key in the authenticated dashboard instead. This operation is available only through the REST API or a GrowSurf API SDK, not through MCP.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Teams","description":"The team bound to an API key or OAuth connection."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"schemas":{"RotateApiKeyResponse":{"type":"object","required":["apiKey"],"properties":{"apiKey":{"type":"string","description":"The new API key. Store it now; the key used for rotation stops working immediately."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/api-key/rotate":{"post":{"tags":["Teams"],"operationId":"rotateApiKey","summary":"Rotate API key","description":"Generates a new API key and makes the key used on this request stop working when rotation succeeds. Send a unique, random `Idempotency-Key`. If the response is interrupted, immediately retry with the original API key and the same `Idempotency-Key` to receive the same new key. Update every integration that used the old key. The team owner is notified by email whenever the key is rotated. GrowSurf SDKs generate the idempotency key automatically. This endpoint accepts an API key with `api_key:rotate`. If this scope is unavailable, rotate the key in the authenticated dashboard instead. This operation is available only through the REST API or a GrowSurf API SDK, not through MCP.","parameters":[{"name":"Idempotency-Key","in":"header","required":true,"description":"A unique, random value for this rotation request. Reuse this value only when retrying the same request with the original API key.","schema":{"type":"string","minLength":8,"maxLength":255,"pattern":"^[A-Za-z0-9._:-]+$"}}],"responses":{"200":{"description":"The new API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RotateApiKeyResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## CAMPAIGNS ↓

{% hint style="info" %}
Create and update a program's identity and lifecycle here (`name`, `companyName`, `companyLogoImageUrl`, `status`, and — on create — `type`, `currencyISO`, and inline `rewards`). A program's currency (`currencyISO`) is chosen when the program is created (defaults to `USD`) and can't be changed afterward — sending it to the update endpoint returns a `400`. A program's design, emails, options, and installation are **not** accepted on create or update and will return a `400`. Edit those — and the program's rewards — through the [Campaign Rewards](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-rewards), [Campaign Design](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-design), [Campaign Emails](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-emails), [Campaign Options](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-options), and [Campaign Installation](https://docs.growsurf.com/developer-tools/rest-api/api-reference#campaign-installation) API endpoints.
{% endhint %}

## List campaigns

> Retrieves a list of your programs. Deleted programs are not returned.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"schemas":{"CampaignListResponse":{"type":"object","required":["campaigns"],"properties":{"campaigns":{"description":"Programs returned for the API key's bound team.","type":"array","items":{"$ref":"#/components/schemas/Campaign"}}}},"Campaign":{"type":"object","description":"Detailed information about a GrowSurf program.","required":["id","name","type","referralCount","participantCount","impressionCount","inviteCount","winnerCount","status","rewards"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the program."},"name":{"type":"string","description":"The program name (internal only, never exposed to participants)."},"type":{"$ref":"#/components/schemas/CampaignType","description":"The program type."},"referralCount":{"type":"integer","readOnly":true,"description":"The total referral count."},"participantCount":{"type":"integer","readOnly":true,"description":"The total participant count."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions — the collective number of times participants' unique referral links have been viewed."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites sent by participants."},"winnerCount":{"type":"integer","readOnly":true,"description":"The total number of winners — all participants with at least one approved reward (includes referrers and referred friends)."},"currencyISO":{"type":["string","null"],"minLength":3,"maxLength":3,"description":"The program currency as an [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) code (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/CampaignStatus","description":"The program status."},"rewards":{"type":"array","items":{"$ref":"#/components/schemas/Reward"},"description":"The list of rewards associated with the program."}}},"CampaignType":{"type":"string","enum":["REFERRAL","AFFILIATE"]},"CampaignStatus":{"type":"string","enum":["DRAFT","IN_PROGRESS","COMPLETE","DELETED"]},"Reward":{"type":"object","description":"A single campaign reward (also known as a `CampaignReward`). This is different from a `ParticipantReward`, which is a reward earned by a participant.","required":["id","type","isUnlimited","metadata"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the campaign reward. You can find this ID from *Program Editor > 1. Rewards* and clicking the reward."},"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type."},"title":{"type":["string","null"],"description":"The reward title (internal only, never exposed to participants)."},"isVisible":{"type":"boolean","description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"description":{"type":["string","null"],"description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (only applicable for double-sided reward types)."},"referredRewardUpfront":{"type":"boolean","description":"Only applies to double-sided rewards. When `true`, the referred friend's reward is delivered upfront as a discount and no `ParticipantReward` is created for them when the referral triggers."},"isUnlimited":{"type":"boolean","description":"`true` if this reward can be earned by a single participant an unlimited number of times."},"limit":{"type":["integer","null"],"description":"The number of times a participant can earn this reward (overridden when `isUnlimited` is `true`). `-1` represents an unlimited reward in REST responses."},"conversionsRequired":{"type":["integer","null"],"description":"The number of referrals a participant must make to earn this reward."},"numberOfWinners":{"type":["integer","null"],"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total."},"limitDuration":{"type":["string","null"],"enum":["IN_TOTAL","PER_MONTH","PER_YEAR",null],"description":"Whether the reward can be earned in total, on a monthly basis, or on a yearly basis."},"imageUrl":{"type":["string","null"],"description":"The reward image URL."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"order":{"type":["integer","null"],"description":"If there are multiple rewards, the order in which the reward should be displayed. `null` by default until set within the Design step of the program editor."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text displayed in front of a participant's referral count for UI purposes (e.g., \"You are only\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text displayed after a participant's referral count for UI purposes (e.g., \"referrals away from receiving a nice reward!\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"The reward metadata."},"commissionStructure":{"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The reward commission structure. Present only for affiliate programs."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. `null` when no valuation is set."}}},"RewardType":{"type":"string","enum":["SINGLE_SIDED","DOUBLE_SIDED","MILESTONE","LEADERBOARD","AFFILIATE"]},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"RewardTaxValuation":{"type":"object","description":"Tax valuation settings for a reward. Only relevant when the program collects tax documentation.","properties":{"fairMarketValueUSD":{"type":["number","null"],"minimum":0,"description":"Manual fair-market value in USD (major units) used as the fallback when the reward value cannot be resolved automatically. `null` = no manual value."},"isTaxReportable":{"type":["boolean","null"],"description":"Whether the reward's value counts toward 1099 thresholds/totals. `null` = use the smart default for the reward's source."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"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"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaigns":{"get":{"tags":["Campaigns"],"operationId":"listCampaigns","summary":"List campaigns","description":"Retrieves a list of your programs. Deleted programs are not returned.","responses":{"200":{"description":"Campaigns returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignListResponse"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Retrieve a campaign

> Retrieves a program for the given program ID.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"Campaign":{"type":"object","description":"Detailed information about a GrowSurf program.","required":["id","name","type","referralCount","participantCount","impressionCount","inviteCount","winnerCount","status","rewards"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the program."},"name":{"type":"string","description":"The program name (internal only, never exposed to participants)."},"type":{"$ref":"#/components/schemas/CampaignType","description":"The program type."},"referralCount":{"type":"integer","readOnly":true,"description":"The total referral count."},"participantCount":{"type":"integer","readOnly":true,"description":"The total participant count."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions — the collective number of times participants' unique referral links have been viewed."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites sent by participants."},"winnerCount":{"type":"integer","readOnly":true,"description":"The total number of winners — all participants with at least one approved reward (includes referrers and referred friends)."},"currencyISO":{"type":["string","null"],"minLength":3,"maxLength":3,"description":"The program currency as an [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) code (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/CampaignStatus","description":"The program status."},"rewards":{"type":"array","items":{"$ref":"#/components/schemas/Reward"},"description":"The list of rewards associated with the program."}}},"CampaignType":{"type":"string","enum":["REFERRAL","AFFILIATE"]},"CampaignStatus":{"type":"string","enum":["DRAFT","IN_PROGRESS","COMPLETE","DELETED"]},"Reward":{"type":"object","description":"A single campaign reward (also known as a `CampaignReward`). This is different from a `ParticipantReward`, which is a reward earned by a participant.","required":["id","type","isUnlimited","metadata"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the campaign reward. You can find this ID from *Program Editor > 1. Rewards* and clicking the reward."},"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type."},"title":{"type":["string","null"],"description":"The reward title (internal only, never exposed to participants)."},"isVisible":{"type":"boolean","description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"description":{"type":["string","null"],"description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (only applicable for double-sided reward types)."},"referredRewardUpfront":{"type":"boolean","description":"Only applies to double-sided rewards. When `true`, the referred friend's reward is delivered upfront as a discount and no `ParticipantReward` is created for them when the referral triggers."},"isUnlimited":{"type":"boolean","description":"`true` if this reward can be earned by a single participant an unlimited number of times."},"limit":{"type":["integer","null"],"description":"The number of times a participant can earn this reward (overridden when `isUnlimited` is `true`). `-1` represents an unlimited reward in REST responses."},"conversionsRequired":{"type":["integer","null"],"description":"The number of referrals a participant must make to earn this reward."},"numberOfWinners":{"type":["integer","null"],"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total."},"limitDuration":{"type":["string","null"],"enum":["IN_TOTAL","PER_MONTH","PER_YEAR",null],"description":"Whether the reward can be earned in total, on a monthly basis, or on a yearly basis."},"imageUrl":{"type":["string","null"],"description":"The reward image URL."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"order":{"type":["integer","null"],"description":"If there are multiple rewards, the order in which the reward should be displayed. `null` by default until set within the Design step of the program editor."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text displayed in front of a participant's referral count for UI purposes (e.g., \"You are only\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text displayed after a participant's referral count for UI purposes (e.g., \"referrals away from receiving a nice reward!\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"The reward metadata."},"commissionStructure":{"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The reward commission structure. Present only for affiliate programs."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. `null` when no valuation is set."}}},"RewardType":{"type":"string","enum":["SINGLE_SIDED","DOUBLE_SIDED","MILESTONE","LEADERBOARD","AFFILIATE"]},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"RewardTaxValuation":{"type":"object","description":"Tax valuation settings for a reward. Only relevant when the program collects tax documentation.","properties":{"fairMarketValueUSD":{"type":["number","null"],"minimum":0,"description":"Manual fair-market value in USD (major units) used as the fallback when the reward value cannot be resolved automatically. `null` = no manual value."},"isTaxReportable":{"type":["boolean","null"],"description":"Whether the reward's value counts toward 1099 thresholds/totals. `null` = use the smart default for the reward's source."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}":{"get":{"tags":["Campaigns"],"operationId":"retrieveCampaign","summary":"Retrieve a campaign","description":"Retrieves a program for the given program ID.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"responses":{"200":{"description":"Campaign returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Create a campaign

> Creates a new program, plus any optional program rewards. The new program is created in \`DRAFT\` status and owned by the API key's bound team.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"schemas":{"CampaignCreateRequest":{"type":"object","description":"Request body for creating a program. Only `type` is required; everything else is server-defaulted. Editor-tab configuration (design, emails, options, installation) is NOT accepted here — configure it via the config sub-resources (e.g. `PATCH /campaign/{id}/options`) after the program is created.","required":["type"],"properties":{"type":{"type":"string","enum":["REFERRAL","AFFILIATE"],"description":"The program type. Immutable after creation."},"name":{"type":"string","description":"The program name (internal only, never exposed to participants). Defaults to a generated name (a friendly label plus the creation date) that you can rename at any time."},"companyName":{"type":"string","description":"The company name shown to participants across the program's emails and referral portal. When omitted, it defaults to the `company` name set when the account was created (see `POST /accounts`), with common legal suffixes removed."},"companyLogoImageUrl":{"type":"string","description":"URL of the company logo shown to participants. When omitted, it is auto-populated from your company's domain (and the program theme is seeded with your brand colors). Pass a value to override the logo; the returned campaign reflects a GrowSurf-hosted URL that may differ from the one you supplied."},"currencyISO":{"type":"string","default":"USD","description":"ISO 4217 currency code. Defaults to `USD`. Chosen when the program is created and immutable afterward — it cannot be changed on update."},"rewards":{"type":"array","items":{"$ref":"#/components/schemas/RewardCreateRequest"},"description":"Optional inline rewards to create with the program."}}},"RewardCreateRequest":{"type":"object","description":"Request body for creating a campaign reward. `type` is required and must be compatible with the program type. AFFILIATE rewards additionally require `commissionStructure` (with `amount` for a `FIXED` commission or `percent` for a `PERCENT` commission).","required":["type"],"allOf":[{"$ref":"#/components/schemas/RewardWritableFields"},{"type":"object","properties":{"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type. Immutable after creation."}}}]},"RewardWritableFields":{"type":"object","description":"The writable fields shared by the create and update campaign reward requests.","properties":{"title":{"type":"string","description":"The reward title (internal only, never exposed to participants)."},"description":{"type":"string","description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (double-sided rewards only)."},"imageUrl":{"type":["string","null"],"description":"An image URL for the reward."},"isVisible":{"type":"boolean","default":true,"description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"isUnlimited":{"type":"boolean","default":true,"description":"Whether the reward can be earned an unlimited number of times. Defaults to `true`, except `MILESTONE` rewards, which can only be earned once."},"referredRewardUpfront":{"type":"boolean","default":false,"description":"For double-sided rewards, deliver the referred friend's reward upfront as a discount."},"limit":{"type":"integer","minimum":0,"default":1,"description":"The number of times a participant can earn the reward (overridden by `isUnlimited`)."},"conversionsRequired":{"type":"integer","minimum":1,"default":1,"description":"The number of referrals required to earn the reward."},"numberOfWinners":{"type":"integer","minimum":0,"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total. A `LEADERBOARD` reward that omits it defaults to `3`."},"order":{"type":"integer","description":"The display order of the reward."},"limitDuration":{"type":"string","enum":["IN_TOTAL","PER_MONTH","PER_YEAR"],"default":"IN_TOTAL","description":"The window over which `limit` applies."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text shown before a participant's referral count in milestone progress copy (e.g. `You are only`). Applies to `MILESTONE` rewards."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text shown after a participant's referral count in milestone progress copy (e.g. `referrals away from your next reward!`). Applies to `MILESTONE` rewards."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook when this reward is earned. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same behavior and caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"Custom key/value metadata (single-level; keys are camelCased on save; values are stored as strings). Sending `metadata` REPLACES the stored object. Campaign copy can reference a key via `{{campaignReward['<rewardId>']['<key>']}}` tokens: renaming a key (same value, new key) automatically rewrites those references; removing a key that campaign copy still references returns a `409` listing the referencing fields."},"commissionStructure":{"$ref":"#/components/schemas/CommissionStructure","description":"The affiliate commission structure (AFFILIATE rewards only). REQUIRED when creating an AFFILIATE reward — include `amount` (and optionally `amountISO`) for a `FIXED` commission, or `percent` for a `PERCENT` commission."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). Used by tax documentation / 1099 reporting. `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. Defaults to not tax-reportable (a purchase rebate). `null` when no valuation is set."}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"RewardTaxValuation":{"type":"object","description":"Tax valuation settings for a reward. Only relevant when the program collects tax documentation.","properties":{"fairMarketValueUSD":{"type":["number","null"],"minimum":0,"description":"Manual fair-market value in USD (major units) used as the fallback when the reward value cannot be resolved automatically. `null` = no manual value."},"isTaxReportable":{"type":["boolean","null"],"description":"Whether the reward's value counts toward 1099 thresholds/totals. `null` = use the smart default for the reward's source."}}},"RewardType":{"type":"string","enum":["SINGLE_SIDED","DOUBLE_SIDED","MILESTONE","LEADERBOARD","AFFILIATE"]},"Campaign":{"type":"object","description":"Detailed information about a GrowSurf program.","required":["id","name","type","referralCount","participantCount","impressionCount","inviteCount","winnerCount","status","rewards"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the program."},"name":{"type":"string","description":"The program name (internal only, never exposed to participants)."},"type":{"$ref":"#/components/schemas/CampaignType","description":"The program type."},"referralCount":{"type":"integer","readOnly":true,"description":"The total referral count."},"participantCount":{"type":"integer","readOnly":true,"description":"The total participant count."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions — the collective number of times participants' unique referral links have been viewed."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites sent by participants."},"winnerCount":{"type":"integer","readOnly":true,"description":"The total number of winners — all participants with at least one approved reward (includes referrers and referred friends)."},"currencyISO":{"type":["string","null"],"minLength":3,"maxLength":3,"description":"The program currency as an [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) code (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/CampaignStatus","description":"The program status."},"rewards":{"type":"array","items":{"$ref":"#/components/schemas/Reward"},"description":"The list of rewards associated with the program."}}},"CampaignType":{"type":"string","enum":["REFERRAL","AFFILIATE"]},"CampaignStatus":{"type":"string","enum":["DRAFT","IN_PROGRESS","COMPLETE","DELETED"]},"Reward":{"type":"object","description":"A single campaign reward (also known as a `CampaignReward`). This is different from a `ParticipantReward`, which is a reward earned by a participant.","required":["id","type","isUnlimited","metadata"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the campaign reward. You can find this ID from *Program Editor > 1. Rewards* and clicking the reward."},"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type."},"title":{"type":["string","null"],"description":"The reward title (internal only, never exposed to participants)."},"isVisible":{"type":"boolean","description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"description":{"type":["string","null"],"description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (only applicable for double-sided reward types)."},"referredRewardUpfront":{"type":"boolean","description":"Only applies to double-sided rewards. When `true`, the referred friend's reward is delivered upfront as a discount and no `ParticipantReward` is created for them when the referral triggers."},"isUnlimited":{"type":"boolean","description":"`true` if this reward can be earned by a single participant an unlimited number of times."},"limit":{"type":["integer","null"],"description":"The number of times a participant can earn this reward (overridden when `isUnlimited` is `true`). `-1` represents an unlimited reward in REST responses."},"conversionsRequired":{"type":["integer","null"],"description":"The number of referrals a participant must make to earn this reward."},"numberOfWinners":{"type":["integer","null"],"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total."},"limitDuration":{"type":["string","null"],"enum":["IN_TOTAL","PER_MONTH","PER_YEAR",null],"description":"Whether the reward can be earned in total, on a monthly basis, or on a yearly basis."},"imageUrl":{"type":["string","null"],"description":"The reward image URL."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"order":{"type":["integer","null"],"description":"If there are multiple rewards, the order in which the reward should be displayed. `null` by default until set within the Design step of the program editor."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text displayed in front of a participant's referral count for UI purposes (e.g., \"You are only\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text displayed after a participant's referral count for UI purposes (e.g., \"referrals away from receiving a nice reward!\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"The reward metadata."},"commissionStructure":{"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The reward commission structure. Present only for affiliate programs."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. `null` when no valuation is set."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaigns":{"post":{"tags":["Campaigns"],"operationId":"createCampaign","summary":"Create a campaign","description":"Creates a new program, plus any optional program rewards. The new program is created in `DRAFT` status and owned by the API key's bound team.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignCreateRequest"}}}},"responses":{"200":{"description":"Campaign created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"402":{"description":"This action isn't available for your account yet (it needs an active subscription or a payment method on file), or the program limit was reached."},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update a campaign

> Updates a program's identity and lifecycle. Only the fields you send are changed. \`type\`, \`urlId\`, and \`currencyISO\` are immutable. Editor-tab configuration (design, emails, options, installation) is edited via the dedicated config sub-resources, not here. The program cannot be deleted via this endpoint.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignUpdateRequest":{"type":"object","description":"Request body for updating a program's identity and lifecycle. Only the fields you send are changed. `type`, `urlId`, and `currencyISO` are immutable — currency is chosen once when the program is created and cannot be changed here (sending it returns a `400`). Editor-tab configuration (design, emails, options, installation) is edited via the dedicated config sub-resources (e.g. `PATCH /campaign/{id}/emails`), not here.","properties":{"name":{"type":"string","description":"The program name (internal only, never exposed to participants)."},"companyName":{"type":"string","description":"The company name shown to participants across the program's emails and referral portal."},"companyLogoImageUrl":{"type":"string","description":"URL of the company logo shown to participants. The returned campaign reflects a GrowSurf-hosted URL that may differ from the one you supplied."},"status":{"type":"string","enum":["IN_PROGRESS","COMPLETE"],"description":"The requested program status. Only the dashboard's lifecycle moves are accepted: `IN_PROGRESS` publishes a `DRAFT` program or resumes a `COMPLETE`/`CANCELLED` one; `COMPLETE` ends an `IN_PROGRESS` program. Any other target (including `DRAFT`, `PENDING`, `CANCELLED`, and `DELETED`) returns a `400`."}}},"Campaign":{"type":"object","description":"Detailed information about a GrowSurf program.","required":["id","name","type","referralCount","participantCount","impressionCount","inviteCount","winnerCount","status","rewards"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the program."},"name":{"type":"string","description":"The program name (internal only, never exposed to participants)."},"type":{"$ref":"#/components/schemas/CampaignType","description":"The program type."},"referralCount":{"type":"integer","readOnly":true,"description":"The total referral count."},"participantCount":{"type":"integer","readOnly":true,"description":"The total participant count."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions — the collective number of times participants' unique referral links have been viewed."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites sent by participants."},"winnerCount":{"type":"integer","readOnly":true,"description":"The total number of winners — all participants with at least one approved reward (includes referrers and referred friends)."},"currencyISO":{"type":["string","null"],"minLength":3,"maxLength":3,"description":"The program currency as an [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) code (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/CampaignStatus","description":"The program status."},"rewards":{"type":"array","items":{"$ref":"#/components/schemas/Reward"},"description":"The list of rewards associated with the program."}}},"CampaignType":{"type":"string","enum":["REFERRAL","AFFILIATE"]},"CampaignStatus":{"type":"string","enum":["DRAFT","IN_PROGRESS","COMPLETE","DELETED"]},"Reward":{"type":"object","description":"A single campaign reward (also known as a `CampaignReward`). This is different from a `ParticipantReward`, which is a reward earned by a participant.","required":["id","type","isUnlimited","metadata"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the campaign reward. You can find this ID from *Program Editor > 1. Rewards* and clicking the reward."},"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type."},"title":{"type":["string","null"],"description":"The reward title (internal only, never exposed to participants)."},"isVisible":{"type":"boolean","description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"description":{"type":["string","null"],"description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (only applicable for double-sided reward types)."},"referredRewardUpfront":{"type":"boolean","description":"Only applies to double-sided rewards. When `true`, the referred friend's reward is delivered upfront as a discount and no `ParticipantReward` is created for them when the referral triggers."},"isUnlimited":{"type":"boolean","description":"`true` if this reward can be earned by a single participant an unlimited number of times."},"limit":{"type":["integer","null"],"description":"The number of times a participant can earn this reward (overridden when `isUnlimited` is `true`). `-1` represents an unlimited reward in REST responses."},"conversionsRequired":{"type":["integer","null"],"description":"The number of referrals a participant must make to earn this reward."},"numberOfWinners":{"type":["integer","null"],"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total."},"limitDuration":{"type":["string","null"],"enum":["IN_TOTAL","PER_MONTH","PER_YEAR",null],"description":"Whether the reward can be earned in total, on a monthly basis, or on a yearly basis."},"imageUrl":{"type":["string","null"],"description":"The reward image URL."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"order":{"type":["integer","null"],"description":"If there are multiple rewards, the order in which the reward should be displayed. `null` by default until set within the Design step of the program editor."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text displayed in front of a participant's referral count for UI purposes (e.g., \"You are only\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text displayed after a participant's referral count for UI purposes (e.g., \"referrals away from receiving a nice reward!\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"The reward metadata."},"commissionStructure":{"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The reward commission structure. Present only for affiliate programs."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. `null` when no valuation is set."}}},"RewardType":{"type":"string","enum":["SINGLE_SIDED","DOUBLE_SIDED","MILESTONE","LEADERBOARD","AFFILIATE"]},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"RewardTaxValuation":{"type":"object","description":"Tax valuation settings for a reward. Only relevant when the program collects tax documentation.","properties":{"fairMarketValueUSD":{"type":["number","null"],"minimum":0,"description":"Manual fair-market value in USD (major units) used as the fallback when the reward value cannot be resolved automatically. `null` = no manual value."},"isTaxReportable":{"type":["boolean","null"],"description":"Whether the reward's value counts toward 1099 thresholds/totals. `null` = use the smart default for the reward's source."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}":{"patch":{"tags":["Campaigns"],"operationId":"updateCampaign","summary":"Update a campaign","description":"Updates a program's identity and lifecycle. Only the fields you send are changed. `type`, `urlId`, and `currencyISO` are immutable. Editor-tab configuration (design, emails, options, installation) is edited via the dedicated config sub-resources, not here. The program cannot be deleted via this endpoint.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignUpdateRequest"}}}},"responses":{"200":{"description":"Campaign updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Clone a campaign

> Clones an existing program into a new \`DRAFT\` program. Integrations and credentials are not copied; active rewards are cloned.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"Campaign":{"type":"object","description":"Detailed information about a GrowSurf program.","required":["id","name","type","referralCount","participantCount","impressionCount","inviteCount","winnerCount","status","rewards"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the program."},"name":{"type":"string","description":"The program name (internal only, never exposed to participants)."},"type":{"$ref":"#/components/schemas/CampaignType","description":"The program type."},"referralCount":{"type":"integer","readOnly":true,"description":"The total referral count."},"participantCount":{"type":"integer","readOnly":true,"description":"The total participant count."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions — the collective number of times participants' unique referral links have been viewed."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites sent by participants."},"winnerCount":{"type":"integer","readOnly":true,"description":"The total number of winners — all participants with at least one approved reward (includes referrers and referred friends)."},"currencyISO":{"type":["string","null"],"minLength":3,"maxLength":3,"description":"The program currency as an [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) code (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/CampaignStatus","description":"The program status."},"rewards":{"type":"array","items":{"$ref":"#/components/schemas/Reward"},"description":"The list of rewards associated with the program."}}},"CampaignType":{"type":"string","enum":["REFERRAL","AFFILIATE"]},"CampaignStatus":{"type":"string","enum":["DRAFT","IN_PROGRESS","COMPLETE","DELETED"]},"Reward":{"type":"object","description":"A single campaign reward (also known as a `CampaignReward`). This is different from a `ParticipantReward`, which is a reward earned by a participant.","required":["id","type","isUnlimited","metadata"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the campaign reward. You can find this ID from *Program Editor > 1. Rewards* and clicking the reward."},"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type."},"title":{"type":["string","null"],"description":"The reward title (internal only, never exposed to participants)."},"isVisible":{"type":"boolean","description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"description":{"type":["string","null"],"description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (only applicable for double-sided reward types)."},"referredRewardUpfront":{"type":"boolean","description":"Only applies to double-sided rewards. When `true`, the referred friend's reward is delivered upfront as a discount and no `ParticipantReward` is created for them when the referral triggers."},"isUnlimited":{"type":"boolean","description":"`true` if this reward can be earned by a single participant an unlimited number of times."},"limit":{"type":["integer","null"],"description":"The number of times a participant can earn this reward (overridden when `isUnlimited` is `true`). `-1` represents an unlimited reward in REST responses."},"conversionsRequired":{"type":["integer","null"],"description":"The number of referrals a participant must make to earn this reward."},"numberOfWinners":{"type":["integer","null"],"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total."},"limitDuration":{"type":["string","null"],"enum":["IN_TOTAL","PER_MONTH","PER_YEAR",null],"description":"Whether the reward can be earned in total, on a monthly basis, or on a yearly basis."},"imageUrl":{"type":["string","null"],"description":"The reward image URL."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"order":{"type":["integer","null"],"description":"If there are multiple rewards, the order in which the reward should be displayed. `null` by default until set within the Design step of the program editor."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text displayed in front of a participant's referral count for UI purposes (e.g., \"You are only\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text displayed after a participant's referral count for UI purposes (e.g., \"referrals away from receiving a nice reward!\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"The reward metadata."},"commissionStructure":{"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The reward commission structure. Present only for affiliate programs."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. `null` when no valuation is set."}}},"RewardType":{"type":"string","enum":["SINGLE_SIDED","DOUBLE_SIDED","MILESTONE","LEADERBOARD","AFFILIATE"]},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"RewardTaxValuation":{"type":"object","description":"Tax valuation settings for a reward. Only relevant when the program collects tax documentation.","properties":{"fairMarketValueUSD":{"type":["number","null"],"minimum":0,"description":"Manual fair-market value in USD (major units) used as the fallback when the reward value cannot be resolved automatically. `null` = no manual value."},"isTaxReportable":{"type":["boolean","null"],"description":"Whether the reward's value counts toward 1099 thresholds/totals. `null` = use the smart default for the reward's source."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/clone":{"post":{"tags":["Campaigns"],"operationId":"cloneCampaign","summary":"Clone a campaign","description":"Clones an existing program into a new `DRAFT` program. Integrations and credentials are not copied; active rewards are cloned.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"responses":{"200":{"description":"Campaign cloned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Campaign"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## CAMPAIGN REWARDS ↓

{% hint style="info" %}
Read or update a program's **Rewards** tab. Available fields depend on the program type (referral or affiliate); `PATCH` is a partial merge (send only what you want to change).
{% endhint %}

## List campaign rewards

> Retrieves the list of a program's configured rewards (\`CampaignReward\`s) — the same set embedded in the \`rewards\` array of the campaign response. Delete a reward with \`DELETE /campaign/{id}/reward-configs/{campaignRewardId}\`.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignRewardListResponse":{"type":"object","description":"The list of a program's configured rewards.","required":["rewards"],"properties":{"rewards":{"type":"array","items":{"$ref":"#/components/schemas/Reward"},"description":"The program's active, visible, and enabled reward configs."}}},"Reward":{"type":"object","description":"A single campaign reward (also known as a `CampaignReward`). This is different from a `ParticipantReward`, which is a reward earned by a participant.","required":["id","type","isUnlimited","metadata"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the campaign reward. You can find this ID from *Program Editor > 1. Rewards* and clicking the reward."},"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type."},"title":{"type":["string","null"],"description":"The reward title (internal only, never exposed to participants)."},"isVisible":{"type":"boolean","description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"description":{"type":["string","null"],"description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (only applicable for double-sided reward types)."},"referredRewardUpfront":{"type":"boolean","description":"Only applies to double-sided rewards. When `true`, the referred friend's reward is delivered upfront as a discount and no `ParticipantReward` is created for them when the referral triggers."},"isUnlimited":{"type":"boolean","description":"`true` if this reward can be earned by a single participant an unlimited number of times."},"limit":{"type":["integer","null"],"description":"The number of times a participant can earn this reward (overridden when `isUnlimited` is `true`). `-1` represents an unlimited reward in REST responses."},"conversionsRequired":{"type":["integer","null"],"description":"The number of referrals a participant must make to earn this reward."},"numberOfWinners":{"type":["integer","null"],"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total."},"limitDuration":{"type":["string","null"],"enum":["IN_TOTAL","PER_MONTH","PER_YEAR",null],"description":"Whether the reward can be earned in total, on a monthly basis, or on a yearly basis."},"imageUrl":{"type":["string","null"],"description":"The reward image URL."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"order":{"type":["integer","null"],"description":"If there are multiple rewards, the order in which the reward should be displayed. `null` by default until set within the Design step of the program editor."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text displayed in front of a participant's referral count for UI purposes (e.g., \"You are only\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text displayed after a participant's referral count for UI purposes (e.g., \"referrals away from receiving a nice reward!\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"The reward metadata."},"commissionStructure":{"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The reward commission structure. Present only for affiliate programs."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. `null` when no valuation is set."}}},"RewardType":{"type":"string","enum":["SINGLE_SIDED","DOUBLE_SIDED","MILESTONE","LEADERBOARD","AFFILIATE"]},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"RewardTaxValuation":{"type":"object","description":"Tax valuation settings for a reward. Only relevant when the program collects tax documentation.","properties":{"fairMarketValueUSD":{"type":["number","null"],"minimum":0,"description":"Manual fair-market value in USD (major units) used as the fallback when the reward value cannot be resolved automatically. `null` = no manual value."},"isTaxReportable":{"type":["boolean","null"],"description":"Whether the reward's value counts toward 1099 thresholds/totals. `null` = use the smart default for the reward's source."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/reward-configs":{"get":{"tags":["Campaigns"],"operationId":"listCampaignRewards","summary":"List campaign rewards","description":"Retrieves the list of a program's configured rewards (`CampaignReward`s) — the same set embedded in the `rewards` array of the campaign response. Delete a reward with `DELETE /campaign/{id}/reward-configs/{campaignRewardId}`.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"responses":{"200":{"description":"Campaign rewards returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignRewardListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Create a campaign reward

> Creates a new campaign reward (\`CampaignReward\`) with a GrowSurf-assigned ID. The reward type must be compatible with the program type (affiliate programs support only \`AFFILIATE\` rewards; referral programs support all other types). Enabling an active reward of a type automatically enables that reward type on the program.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"RewardCreateRequest":{"type":"object","description":"Request body for creating a campaign reward. `type` is required and must be compatible with the program type. AFFILIATE rewards additionally require `commissionStructure` (with `amount` for a `FIXED` commission or `percent` for a `PERCENT` commission).","required":["type"],"allOf":[{"$ref":"#/components/schemas/RewardWritableFields"},{"type":"object","properties":{"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type. Immutable after creation."}}}]},"RewardWritableFields":{"type":"object","description":"The writable fields shared by the create and update campaign reward requests.","properties":{"title":{"type":"string","description":"The reward title (internal only, never exposed to participants)."},"description":{"type":"string","description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (double-sided rewards only)."},"imageUrl":{"type":["string","null"],"description":"An image URL for the reward."},"isVisible":{"type":"boolean","default":true,"description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"isUnlimited":{"type":"boolean","default":true,"description":"Whether the reward can be earned an unlimited number of times. Defaults to `true`, except `MILESTONE` rewards, which can only be earned once."},"referredRewardUpfront":{"type":"boolean","default":false,"description":"For double-sided rewards, deliver the referred friend's reward upfront as a discount."},"limit":{"type":"integer","minimum":0,"default":1,"description":"The number of times a participant can earn the reward (overridden by `isUnlimited`)."},"conversionsRequired":{"type":"integer","minimum":1,"default":1,"description":"The number of referrals required to earn the reward."},"numberOfWinners":{"type":"integer","minimum":0,"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total. A `LEADERBOARD` reward that omits it defaults to `3`."},"order":{"type":"integer","description":"The display order of the reward."},"limitDuration":{"type":"string","enum":["IN_TOTAL","PER_MONTH","PER_YEAR"],"default":"IN_TOTAL","description":"The window over which `limit` applies."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text shown before a participant's referral count in milestone progress copy (e.g. `You are only`). Applies to `MILESTONE` rewards."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text shown after a participant's referral count in milestone progress copy (e.g. `referrals away from your next reward!`). Applies to `MILESTONE` rewards."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook when this reward is earned. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same behavior and caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"Custom key/value metadata (single-level; keys are camelCased on save; values are stored as strings). Sending `metadata` REPLACES the stored object. Campaign copy can reference a key via `{{campaignReward['<rewardId>']['<key>']}}` tokens: renaming a key (same value, new key) automatically rewrites those references; removing a key that campaign copy still references returns a `409` listing the referencing fields."},"commissionStructure":{"$ref":"#/components/schemas/CommissionStructure","description":"The affiliate commission structure (AFFILIATE rewards only). REQUIRED when creating an AFFILIATE reward — include `amount` (and optionally `amountISO`) for a `FIXED` commission, or `percent` for a `PERCENT` commission."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). Used by tax documentation / 1099 reporting. `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. Defaults to not tax-reportable (a purchase rebate). `null` when no valuation is set."}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"RewardTaxValuation":{"type":"object","description":"Tax valuation settings for a reward. Only relevant when the program collects tax documentation.","properties":{"fairMarketValueUSD":{"type":["number","null"],"minimum":0,"description":"Manual fair-market value in USD (major units) used as the fallback when the reward value cannot be resolved automatically. `null` = no manual value."},"isTaxReportable":{"type":["boolean","null"],"description":"Whether the reward's value counts toward 1099 thresholds/totals. `null` = use the smart default for the reward's source."}}},"RewardType":{"type":"string","enum":["SINGLE_SIDED","DOUBLE_SIDED","MILESTONE","LEADERBOARD","AFFILIATE"]},"Reward":{"type":"object","description":"A single campaign reward (also known as a `CampaignReward`). This is different from a `ParticipantReward`, which is a reward earned by a participant.","required":["id","type","isUnlimited","metadata"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the campaign reward. You can find this ID from *Program Editor > 1. Rewards* and clicking the reward."},"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type."},"title":{"type":["string","null"],"description":"The reward title (internal only, never exposed to participants)."},"isVisible":{"type":"boolean","description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"description":{"type":["string","null"],"description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (only applicable for double-sided reward types)."},"referredRewardUpfront":{"type":"boolean","description":"Only applies to double-sided rewards. When `true`, the referred friend's reward is delivered upfront as a discount and no `ParticipantReward` is created for them when the referral triggers."},"isUnlimited":{"type":"boolean","description":"`true` if this reward can be earned by a single participant an unlimited number of times."},"limit":{"type":["integer","null"],"description":"The number of times a participant can earn this reward (overridden when `isUnlimited` is `true`). `-1` represents an unlimited reward in REST responses."},"conversionsRequired":{"type":["integer","null"],"description":"The number of referrals a participant must make to earn this reward."},"numberOfWinners":{"type":["integer","null"],"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total."},"limitDuration":{"type":["string","null"],"enum":["IN_TOTAL","PER_MONTH","PER_YEAR",null],"description":"Whether the reward can be earned in total, on a monthly basis, or on a yearly basis."},"imageUrl":{"type":["string","null"],"description":"The reward image URL."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"order":{"type":["integer","null"],"description":"If there are multiple rewards, the order in which the reward should be displayed. `null` by default until set within the Design step of the program editor."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text displayed in front of a participant's referral count for UI purposes (e.g., \"You are only\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text displayed after a participant's referral count for UI purposes (e.g., \"referrals away from receiving a nice reward!\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"The reward metadata."},"commissionStructure":{"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The reward commission structure. Present only for affiliate programs."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. `null` when no valuation is set."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/reward-configs":{"post":{"tags":["Campaigns"],"operationId":"createCampaignReward","summary":"Create a campaign reward","description":"Creates a new campaign reward (`CampaignReward`) with a GrowSurf-assigned ID. The reward type must be compatible with the program type (affiliate programs support only `AFFILIATE` rewards; referral programs support all other types). Enabling an active reward of a type automatically enables that reward type on the program.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewardCreateRequest"}}}},"responses":{"200":{"description":"Campaign reward created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reward"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update a campaign reward

> Updates an existing campaign reward (\`CampaignReward\`). The reward \`type\` is immutable and cannot be changed. When the update replaces \`metadata\`, renamed keys automatically rewrite any \`{{campaignReward\[…]}}\` references in campaign copy; removing a key that campaign copy still references returns a \`409\` listing the referencing fields.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"CampaignRewardId":{"name":"campaignRewardId","in":"path","required":true,"description":"Campaign reward (`CampaignReward`) ID.","schema":{"type":"string"}}},"schemas":{"RewardUpdateRequest":{"allOf":[{"$ref":"#/components/schemas/RewardWritableFields"}],"description":"Request body for updating a campaign reward. All fields are optional; `type` is immutable and must not be supplied."},"RewardWritableFields":{"type":"object","description":"The writable fields shared by the create and update campaign reward requests.","properties":{"title":{"type":"string","description":"The reward title (internal only, never exposed to participants)."},"description":{"type":"string","description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (double-sided rewards only)."},"imageUrl":{"type":["string","null"],"description":"An image URL for the reward."},"isVisible":{"type":"boolean","default":true,"description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"isUnlimited":{"type":"boolean","default":true,"description":"Whether the reward can be earned an unlimited number of times. Defaults to `true`, except `MILESTONE` rewards, which can only be earned once."},"referredRewardUpfront":{"type":"boolean","default":false,"description":"For double-sided rewards, deliver the referred friend's reward upfront as a discount."},"limit":{"type":"integer","minimum":0,"default":1,"description":"The number of times a participant can earn the reward (overridden by `isUnlimited`)."},"conversionsRequired":{"type":"integer","minimum":1,"default":1,"description":"The number of referrals required to earn the reward."},"numberOfWinners":{"type":"integer","minimum":0,"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total. A `LEADERBOARD` reward that omits it defaults to `3`."},"order":{"type":"integer","description":"The display order of the reward."},"limitDuration":{"type":"string","enum":["IN_TOTAL","PER_MONTH","PER_YEAR"],"default":"IN_TOTAL","description":"The window over which `limit` applies."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text shown before a participant's referral count in milestone progress copy (e.g. `You are only`). Applies to `MILESTONE` rewards."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text shown after a participant's referral count in milestone progress copy (e.g. `referrals away from your next reward!`). Applies to `MILESTONE` rewards."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook when this reward is earned. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same behavior and caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"Custom key/value metadata (single-level; keys are camelCased on save; values are stored as strings). Sending `metadata` REPLACES the stored object. Campaign copy can reference a key via `{{campaignReward['<rewardId>']['<key>']}}` tokens: renaming a key (same value, new key) automatically rewrites those references; removing a key that campaign copy still references returns a `409` listing the referencing fields."},"commissionStructure":{"$ref":"#/components/schemas/CommissionStructure","description":"The affiliate commission structure (AFFILIATE rewards only). REQUIRED when creating an AFFILIATE reward — include `amount` (and optionally `amountISO`) for a `FIXED` commission, or `percent` for a `PERCENT` commission."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). Used by tax documentation / 1099 reporting. `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. Defaults to not tax-reportable (a purchase rebate). `null` when no valuation is set."}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"RewardTaxValuation":{"type":"object","description":"Tax valuation settings for a reward. Only relevant when the program collects tax documentation.","properties":{"fairMarketValueUSD":{"type":["number","null"],"minimum":0,"description":"Manual fair-market value in USD (major units) used as the fallback when the reward value cannot be resolved automatically. `null` = no manual value."},"isTaxReportable":{"type":["boolean","null"],"description":"Whether the reward's value counts toward 1099 thresholds/totals. `null` = use the smart default for the reward's source."}}},"Reward":{"type":"object","description":"A single campaign reward (also known as a `CampaignReward`). This is different from a `ParticipantReward`, which is a reward earned by a participant.","required":["id","type","isUnlimited","metadata"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the campaign reward. You can find this ID from *Program Editor > 1. Rewards* and clicking the reward."},"type":{"$ref":"#/components/schemas/RewardType","description":"The reward type."},"title":{"type":["string","null"],"description":"The reward title (internal only, never exposed to participants)."},"isVisible":{"type":"boolean","description":"Whether the reward is enabled. When `false`, the reward is disabled: it is no longer awarded, and it is hidden from participants, including those who already earned it. Set `true` for the reward to be visible and awardable."},"description":{"type":["string","null"],"description":"The reward description shown to the referrer."},"referralDescription":{"type":["string","null"],"description":"The reward description shown to the referred friend (only applicable for double-sided reward types)."},"referredRewardUpfront":{"type":"boolean","description":"Only applies to double-sided rewards. When `true`, the referred friend's reward is delivered upfront as a discount and no `ParticipantReward` is created for them when the referral triggers."},"isUnlimited":{"type":"boolean","description":"`true` if this reward can be earned by a single participant an unlimited number of times."},"limit":{"type":["integer","null"],"description":"The number of times a participant can earn this reward (overridden when `isUnlimited` is `true`). `-1` represents an unlimited reward in REST responses."},"conversionsRequired":{"type":["integer","null"],"description":"The number of referrals a participant must make to earn this reward."},"numberOfWinners":{"type":["integer","null"],"description":"The maximum number of winners. Only applies to `LEADERBOARD` rewards. When `limitDuration` is `PER_MONTH`, this many top referrers win each month; otherwise this many win in total."},"limitDuration":{"type":["string","null"],"enum":["IN_TOTAL","PER_MONTH","PER_YEAR",null],"description":"Whether the reward can be earned in total, on a monthly basis, or on a yearly basis."},"imageUrl":{"type":["string","null"],"description":"The reward image URL."},"couponCode":{"type":["string","null"],"description":"A static coupon code shown to the referrer in the reward-won email and webhook. Display text only; GrowSurf does not create or validate it in any billing system. If the program has a connected billing integration (Stripe, Chargebee, or Recurly) that issues a coupon for the referral, that issued code is shown instead."},"order":{"type":["integer","null"],"description":"If there are multiple rewards, the order in which the reward should be displayed. `null` by default until set within the Design step of the program editor."},"nextMilestonePrefix":{"type":["string","null"],"description":"Text displayed in front of a participant's referral count for UI purposes (e.g., \"You are only\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"nextMilestoneSuffix":{"type":["string","null"],"description":"Text displayed after a participant's referral count for UI purposes (e.g., \"referrals away from receiving a nice reward!\"). Applicable for milestone rewards (when `type` is `MILESTONE`)."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"The reward metadata."},"commissionStructure":{"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The reward commission structure. Present only for affiliate programs."},"referralCouponCode":{"type":["string","null"],"description":"A static coupon code shown to the referred friend in the reward-won email and webhook (double-sided rewards). Same caveats as `couponCode`: display text only, not created or validated in any billing system, and superseded by a connected billing integration's issued coupon when one exists."},"value":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the reward (the referrer's side of a double-sided reward). `null` when no valuation is set."},"referredValue":{"oneOf":[{"$ref":"#/components/schemas/RewardTaxValuation"},{"type":"null"}],"description":"Tax valuation for the referred friend's side of a double-sided reward. `null` when no valuation is set."}}},"RewardType":{"type":"string","enum":["SINGLE_SIDED","DOUBLE_SIDED","MILESTONE","LEADERBOARD","AFFILIATE"]},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflicting duplicate request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/reward-configs/{campaignRewardId}":{"patch":{"tags":["Campaigns"],"operationId":"updateCampaignReward","summary":"Update a campaign reward","description":"Updates an existing campaign reward (`CampaignReward`). The reward `type` is immutable and cannot be changed. When the update replaces `metadata`, renamed keys automatically rewrite any `{{campaignReward[…]}}` references in campaign copy; removing a key that campaign copy still references returns a `409` listing the referencing fields.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/CampaignRewardId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RewardUpdateRequest"}}}},"responses":{"200":{"description":"Campaign reward updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Reward"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Delete a campaign reward

> Deletes a campaign reward (\`CampaignReward\`). The reward is deactivated, removed from the program's reward set, and any connected upfront-discount coupons are cleaned up. If campaign copy still references any of the reward's metadata keys via \`{{campaignReward\[…]}}\` tokens, the delete returns a \`409\` listing the referencing fields — update those fields first.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"CampaignRewardId":{"name":"campaignRewardId","in":"path","required":true,"description":"Campaign reward (`CampaignReward`) ID.","schema":{"type":"string"}}},"schemas":{"DeleteRewardResponse":{"type":"object","required":["id","success"],"properties":{"id":{"type":"string","description":"The deleted reward ID."},"success":{"description":"Whether the campaign reward was deleted.","type":"boolean"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflicting duplicate request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/reward-configs/{campaignRewardId}":{"delete":{"tags":["Campaigns"],"operationId":"deleteCampaignReward","summary":"Delete a campaign reward","description":"Deletes a campaign reward (`CampaignReward`). The reward is deactivated, removed from the program's reward set, and any connected upfront-discount coupons are cleaned up. If campaign copy still references any of the reward's metadata keys via `{{campaignReward[…]}}` tokens, the delete returns a `409` listing the referencing fields — update those fields first.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/CampaignRewardId"}],"responses":{"200":{"description":"Campaign reward deleted.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeleteRewardResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## CAMPAIGN DESIGN ↓

{% hint style="info" %}
Read or update a program's **Design** tab — the GrowSurf window layout, header, share channels, signup form, portal/landing pages, and theme styling. This is a large, deeply nested object whose available fields depend on the program type (referral or affiliate); `PATCH` is a partial merge (send only what you want to change). Portal/landing-page custom code/JS is not editable via the API.
{% endhint %}

## Retrieve campaign design

> Retrieves a program's design configuration — the same surface as the dashboard Program Editor's \*\*Design\*\* tab: the GrowSurf window layout, header, share channels + invite, signup form, portal/landing pages, theme styling, and the referral/affiliate summary + status sections. This is a large object whose available fields depend on the program type; the response includes every field and its current value, which is the same shape you send back on \`PATCH\`.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignDesign":{"type":"object","description":"A program's design configuration (the dashboard Program Editor's Design tab). This is a large, nested object organized by section; each section holds the design fields for that part of the GrowSurf window and landing pages. The exact fields available depend on the program type (e.g. `referralSummary` is referral-only; `affiliateSummary`, `commissions`, `payouts`, and `participantSettings` are affiliate-only). To see the full object with every field and its current value, `GET` this resource, then `PATCH` back only the sections or fields you want to change (arrays like `signup.fields` replace wholesale).","properties":{"window":{"type":"object","description":"How the sections inside the GrowSurf window are laid out. `navigationMode` chooses between a tabbed layout (`TABS`) and a single scrolling list (`LIST`)."},"header":{"type":"object","description":"Header content for participants (`postText`) and non-participants (`preText`)."},"stats":{"type":"object","description":"The stats panel a participant sees at the top of the GrowSurf window, summarizing their own referral progress. Only its heading text (`title`) is editable here."},"share":{"type":"object","description":"Share channels (`type.<channel>.{isVisible,buttonText,message,order}`), invite settings, and share-button styling."},"signup":{"type":"object","description":"Signup form (`fields[]`, GDPR consent, button and login text)."},"referralStatus":{"type":"object","description":"The Referral Status section, which lists the people a participant has invited and each one's progress (invite sent, pending, awarded, or expired). Controls its title, icon, column and status labels, empty-state text, and whether invited emails are masked."},"leaderboard":{"type":"object","description":"The Leaderboard section that ranks participants against each other. Controls its title, icon, column labels, which time-period and ranking selectors appear, empty-state text, and how other participants' names are masked."},"referredExperience":{"type":"object","description":"The banner and headline shown to someone who arrives on your site through a referral link (for example, \"You were referred by Jane\"). Controls the banner and heading text, where they appear, whether they link to signup, and how the referrer's name is formatted."},"referralSummary":{"type":"object","description":"Referral programs only. The row of summary tiles a participant sees (clicks, leads, referrals, invites sent, rewards earned, and more). Controls each tile's label, tooltip, and visibility, plus the section's title and icon."},"affiliateSummary":{"type":"object","description":"Affiliate programs only. The row of summary tiles an affiliate sees (clicks, leads, referrals, referral revenue, upcoming payout, and total paid out). Controls each tile's label, tooltip, and visibility, plus the section's title and icon."},"commissions":{"type":"object","description":"Affiliate programs only. The Commissions section of the participant portal, which lists an affiliate's earned commissions and their status (pending, approved, paid, or reversed). Controls its title, icon, column labels, status labels and tooltips, and empty-state text."},"payouts":{"type":"object","description":"Affiliate programs only. The Payouts section of the participant portal, which lists an affiliate's payouts and their status (upcoming, queued, issued, or failed). Controls its title, icon, column labels, status labels and tooltips, and empty-state text."},"rewards":{"type":"object","description":"The heading, icon, and empty-state text of the rewards panel shown to participants. Which rewards appear, and the order they appear in, are managed through the campaign rewards endpoints, not here."},"participantSettings":{"type":"object","description":"The participant's account settings area in the portal. Controls the labels and messages for logging out, managing a PayPal payout email, and completing tax details (country of residency, VAT, and tax-form status). Applies to affiliate programs, and to referral programs that collect tax information or pay participants through PayPal."},"landingPages":{"type":"object","description":"Portal and landing pages — company info, `content` (FAQ/terms/labels), `styles` (fonts), third-party script IDs (GA/GTM/Facebook/HubSpot), and SEO meta tags. Custom code, CSS, and JS are not editable via the API."},"theme":{"type":"object","description":"Visual theme styling (colors, shadows, and similar)."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/design":{"get":{"tags":["Campaigns"],"operationId":"retrieveCampaignDesign","summary":"Retrieve campaign design","description":"Retrieves a program's design configuration — the same surface as the dashboard Program Editor's **Design** tab: the GrowSurf window layout, header, share channels + invite, signup form, portal/landing pages, theme styling, and the referral/affiliate summary + status sections. This is a large object whose available fields depend on the program type; the response includes every field and its current value, which is the same shape you send back on `PATCH`.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"responses":{"200":{"description":"Campaign design returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignDesign"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update campaign design

> Updates a program's design configuration. Only the fields you send are changed; anything you leave out is untouched (arrays such as \`signup.fields\` replace wholesale). Unknown fields, fields not available for the program type, and invalid values return a \`400\`. Landing-page custom code and JavaScript are not editable via the API.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignDesign":{"type":"object","description":"A program's design configuration (the dashboard Program Editor's Design tab). This is a large, nested object organized by section; each section holds the design fields for that part of the GrowSurf window and landing pages. The exact fields available depend on the program type (e.g. `referralSummary` is referral-only; `affiliateSummary`, `commissions`, `payouts`, and `participantSettings` are affiliate-only). To see the full object with every field and its current value, `GET` this resource, then `PATCH` back only the sections or fields you want to change (arrays like `signup.fields` replace wholesale).","properties":{"window":{"type":"object","description":"How the sections inside the GrowSurf window are laid out. `navigationMode` chooses between a tabbed layout (`TABS`) and a single scrolling list (`LIST`)."},"header":{"type":"object","description":"Header content for participants (`postText`) and non-participants (`preText`)."},"stats":{"type":"object","description":"The stats panel a participant sees at the top of the GrowSurf window, summarizing their own referral progress. Only its heading text (`title`) is editable here."},"share":{"type":"object","description":"Share channels (`type.<channel>.{isVisible,buttonText,message,order}`), invite settings, and share-button styling."},"signup":{"type":"object","description":"Signup form (`fields[]`, GDPR consent, button and login text)."},"referralStatus":{"type":"object","description":"The Referral Status section, which lists the people a participant has invited and each one's progress (invite sent, pending, awarded, or expired). Controls its title, icon, column and status labels, empty-state text, and whether invited emails are masked."},"leaderboard":{"type":"object","description":"The Leaderboard section that ranks participants against each other. Controls its title, icon, column labels, which time-period and ranking selectors appear, empty-state text, and how other participants' names are masked."},"referredExperience":{"type":"object","description":"The banner and headline shown to someone who arrives on your site through a referral link (for example, \"You were referred by Jane\"). Controls the banner and heading text, where they appear, whether they link to signup, and how the referrer's name is formatted."},"referralSummary":{"type":"object","description":"Referral programs only. The row of summary tiles a participant sees (clicks, leads, referrals, invites sent, rewards earned, and more). Controls each tile's label, tooltip, and visibility, plus the section's title and icon."},"affiliateSummary":{"type":"object","description":"Affiliate programs only. The row of summary tiles an affiliate sees (clicks, leads, referrals, referral revenue, upcoming payout, and total paid out). Controls each tile's label, tooltip, and visibility, plus the section's title and icon."},"commissions":{"type":"object","description":"Affiliate programs only. The Commissions section of the participant portal, which lists an affiliate's earned commissions and their status (pending, approved, paid, or reversed). Controls its title, icon, column labels, status labels and tooltips, and empty-state text."},"payouts":{"type":"object","description":"Affiliate programs only. The Payouts section of the participant portal, which lists an affiliate's payouts and their status (upcoming, queued, issued, or failed). Controls its title, icon, column labels, status labels and tooltips, and empty-state text."},"rewards":{"type":"object","description":"The heading, icon, and empty-state text of the rewards panel shown to participants. Which rewards appear, and the order they appear in, are managed through the campaign rewards endpoints, not here."},"participantSettings":{"type":"object","description":"The participant's account settings area in the portal. Controls the labels and messages for logging out, managing a PayPal payout email, and completing tax details (country of residency, VAT, and tax-form status). Applies to affiliate programs, and to referral programs that collect tax information or pay participants through PayPal."},"landingPages":{"type":"object","description":"Portal and landing pages — company info, `content` (FAQ/terms/labels), `styles` (fonts), third-party script IDs (GA/GTM/Facebook/HubSpot), and SEO meta tags. Custom code, CSS, and JS are not editable via the API."},"theme":{"type":"object","description":"Visual theme styling (colors, shadows, and similar)."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/design":{"patch":{"tags":["Campaigns"],"operationId":"updateCampaignDesign","summary":"Update campaign design","description":"Updates a program's design configuration. Only the fields you send are changed; anything you leave out is untouched (arrays such as `signup.fields` replace wholesale). Unknown fields, fields not available for the program type, and invalid values return a `400`. Landing-page custom code and JavaScript are not editable via the API.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignDesign"}}}},"responses":{"200":{"description":"Campaign design updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignDesign"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## CAMPAIGN EMAILS ↓

{% hint style="info" %}
Read or update a program's **Emails** tab — each editable email template (`subject`, `preheader`, `body`, `isEnabled`) plus the shared `settings` block (sender, contact, and design). The set of templates depends on the program type (referral or affiliate). `PATCH` is a partial merge (send only what you want to change); writing a template that is not available for the program type (referral or affiliate) returns a `400`, and the custom sender `fromEmail` is read-only (it requires dashboard domain verification).
{% endhint %}

## Retrieve campaign emails

> Retrieves a program's email configuration — the same surface as the dashboard Program Editor's \*\*Emails\*\* tab. Returns each editable email template (\`subject\`, \`preheader\`, \`body\`, \`isEnabled\`) plus the \`settings\` block (sender, contact, and design). The set of email templates returned depends on the program type (referral vs affiliate).

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignEmails":{"type":"object","description":"A program's email configuration (the dashboard Program Editor's Emails tab). Each property is an editable email template; the set of templates available depends on the program type (referral vs affiliate), and writing a template that is not available for the program type returns a `400`. The `settings` object holds sender, contact, and design settings. To see the full object with every template and its current values, `GET` this resource, then `PATCH` back only the fields you want to change.","properties":{"welcomeNonReferred":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Welcome email sent to a participant who joins on their own, without being referred. Available for both referral and affiliate programs."},"welcomeReferred":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Welcome email sent to someone who signs up through another participant's referral link (also covers referrals you add from the dashboard). Referral programs only."},"referralLinkViewedFirstTime":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to a participant the first time their referral link is viewed. It goes out only once. Available for both referral and affiliate programs."},"referralLinkUsed":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to a referrer when they earn referral credit because someone they referred completed the qualifying action. Most useful when a reward needs more than one referral to unlock. Labeled \"Referral Credit Received\" in the Program Editor. Referral programs only."},"referredSignup":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to a referrer each time someone signs up using their referral link. Labeled \"Referral Link Used\" in the Program Editor. Available for both referral and affiliate programs."},"goalAchieved":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to a participant when they unlock a reward (and to the referred friend as well, for double-sided rewards). Labeled \"Reward Unlocked\" in the Program Editor. Referral programs only."},"campaignEndedWinners":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to every participant who unlocked at least one reward, once the program ends. Referral programs only."},"campaignEndedNonWinners":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to every participant who did not unlock a reward, once the program ends. Referral programs only."},"progressUpdateMonthly":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"A month-end summary emailed to participants recapping how they are doing in the program. Available for both referral and affiliate programs."},"commissionGenerated":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to an affiliate when they earn a new commission, for example when a customer they referred makes a purchase. Affiliate programs only."},"commissionAdjusted":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to an affiliate when one of their commissions is adjusted because of a full refund, partial refund, or chargeback. Affiliate programs only."},"payoutPending":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to an affiliate when a batch payment is started and they have a payout on the way. Affiliate programs only."},"payoutSentSuccess":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to an affiliate when their payout completes successfully. Labeled \"Payout Sent Successfully\" in the Program Editor. Affiliate programs only."},"invite":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"The invitation email a participant sends to friends. Applies only when the program sends invites on the company's behalf (Design > Share > Invite, with your company as the sender). Its `useCompanyReplyTo` field sets whether replies go to your company or to the participant who sent the invite. Available for both referral and affiliate programs."},"loginLink":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"The one-time sign-in link emailed to returning participants. Applies when participant sign-in is required (in Options). Labeled \"One-Time Login Link\" in the Program Editor. This is a transactional email, so its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"paypalEmailConfirmation":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Asks a participant to set or confirm the PayPal email address where they will receive payouts. Applies when the confirm-email setting is on in the PayPal integration. This is a transactional email, so its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"taxInfoMissing":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Asks a participant to submit the tax information required before they can be paid. Sends only when the program collects tax information. Its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"taxInfoReceived":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Confirms to a participant that the tax information they submitted was received. Its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"taxInfoApproved":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Tells a participant that their tax form is complete and approved. Labeled \"Tax Form Complete\" in the Program Editor. Its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"taxInfoRejected":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Tells a participant that their tax information was rejected and needs to be resubmitted. Its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"settings":{"$ref":"#/components/schemas/CampaignEmailSettings","description":"Sender, contact, and design settings for the program's emails (the Emails > Settings sub-page)."}}},"CampaignEmailTemplate":{"type":"object","description":"A single editable email template. `subject` and `preheader` are capped at 255 characters; `body` is HTML capped at 150000 characters.","properties":{"subject":{"type":"string","maxLength":255,"description":"The email's subject line. Supports dynamic text (`{{...}}` tokens), the same as the body."},"preheader":{"type":"string","maxLength":255,"description":"The preview text shown after the subject line in most inboxes."},"body":{"type":"string","maxLength":150000,"description":"The email body as HTML. You can personalize it with dynamic text, inserting `{{...}}` tokens like `{{firstName}}` or `{{shareUrl}}` to tailor each email to its recipient. See [Guide to using dynamic text in GrowSurf emails](https://support.growsurf.com/article/213-guide-to-using-dynamic-text-in-growsurf-emails)."},"isEnabled":{"type":"boolean","description":"Whether this email is enabled. Read-only for transactional and invite emails (only promotional emails can be toggled) — sending it for those emails returns a `400`."},"useCompanyReplyTo":{"type":"boolean","description":"Only applicable to the `invite` email. When `true`, replies go to your company's reply-to address instead of the referring participant, which keeps the participant's email address private (it is never exposed to the people they invite). When `false`, replies go to the referring participant."}}},"CampaignEmailSettings":{"type":"object","description":"Email sender, contact, and design settings (the Emails → Settings sub-page).","properties":{"sender":{"description":"Sender name and reply-to settings for program emails.","$ref":"#/components/schemas/CampaignEmailSenderSettings"},"contact":{"$ref":"#/components/schemas/CampaignEmailContactSettings"},"design":{"description":"Design settings shared across program emails.","$ref":"#/components/schemas/CampaignEmailDesignSettings"}}},"CampaignEmailSenderSettings":{"type":"object","properties":{"fromName":{"type":"string","maxLength":100,"description":"The email sender name. Required; cannot be an email address."},"replyToEmail":{"type":"string","maxLength":100,"description":"The reply-to email address. Required; must be a valid email address."},"fromEmail":{"type":"string","readOnly":true,"description":"The from email address. Read-only — setting a custom from-address requires domain verification in the dashboard."}}},"CampaignEmailContactSettings":{"type":"object","description":"The physical mailing address shown in your email footers (required by anti-spam laws such as the US CAN-SPAM Act).","properties":{"companyName":{"type":"string","maxLength":255,"description":"Required."},"addressLine1":{"type":"string","maxLength":255,"description":"Required."},"addressLine2":{"description":"Second address line shown in email footers.","type":["string","null"],"maxLength":255},"city":{"type":"string","maxLength":255,"description":"Required."},"state":{"description":"State, province, or region shown in email footers.","type":["string","null"],"maxLength":255},"postalCode":{"description":"Postal or ZIP code shown in email footers.","type":["string","null"],"maxLength":100},"country":{"type":["string","null"],"maxLength":100,"description":"Free-text country (not validated as an ISO code)."}}},"CampaignEmailDesignSettings":{"type":"object","properties":{"header":{"type":["string","null"],"maxLength":150000,"description":"Email header HTML."},"footer":{"type":["string","null"],"maxLength":150000,"description":"Email footer HTML."},"unsubscribePromotional":{"type":"string","maxLength":150000,"description":"Unsubscribe footer for promotional emails. Must contain the tokens `{{unsubscribeLink}}`, `{{companyName}}`, and `{{address}}`."},"unsubscribeInvite":{"type":"string","maxLength":150000,"description":"Unsubscribe footer for invite emails. Must contain the tokens `{{unsubscribeLink}}`, `{{companyName}}`, and `{{address}}`."},"unsubscribeTransactional":{"type":"string","maxLength":150000,"description":"Footer for transactional emails. Must contain the tokens `{{companyName}}` and `{{address}}`."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/emails":{"get":{"tags":["Campaigns"],"operationId":"retrieveCampaignEmails","summary":"Retrieve campaign emails","description":"Retrieves a program's email configuration — the same surface as the dashboard Program Editor's **Emails** tab. Returns each editable email template (`subject`, `preheader`, `body`, `isEnabled`) plus the `settings` block (sender, contact, and design). The set of email templates returned depends on the program type (referral vs affiliate).","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"responses":{"200":{"description":"Campaign emails configuration returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignEmails"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update campaign emails

> Updates a program's email configuration. Only the fields you send are changed; omitted fields are left untouched. You may only write the email templates the dashboard exposes for the program type — writing a template that is not available for the program type returns a \`400\`. Some fields are read-only (\`settings.sender.fromEmail\`, whose custom value requires dashboard domain verification).

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignEmails":{"type":"object","description":"A program's email configuration (the dashboard Program Editor's Emails tab). Each property is an editable email template; the set of templates available depends on the program type (referral vs affiliate), and writing a template that is not available for the program type returns a `400`. The `settings` object holds sender, contact, and design settings. To see the full object with every template and its current values, `GET` this resource, then `PATCH` back only the fields you want to change.","properties":{"welcomeNonReferred":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Welcome email sent to a participant who joins on their own, without being referred. Available for both referral and affiliate programs."},"welcomeReferred":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Welcome email sent to someone who signs up through another participant's referral link (also covers referrals you add from the dashboard). Referral programs only."},"referralLinkViewedFirstTime":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to a participant the first time their referral link is viewed. It goes out only once. Available for both referral and affiliate programs."},"referralLinkUsed":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to a referrer when they earn referral credit because someone they referred completed the qualifying action. Most useful when a reward needs more than one referral to unlock. Labeled \"Referral Credit Received\" in the Program Editor. Referral programs only."},"referredSignup":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to a referrer each time someone signs up using their referral link. Labeled \"Referral Link Used\" in the Program Editor. Available for both referral and affiliate programs."},"goalAchieved":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to a participant when they unlock a reward (and to the referred friend as well, for double-sided rewards). Labeled \"Reward Unlocked\" in the Program Editor. Referral programs only."},"campaignEndedWinners":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to every participant who unlocked at least one reward, once the program ends. Referral programs only."},"campaignEndedNonWinners":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to every participant who did not unlock a reward, once the program ends. Referral programs only."},"progressUpdateMonthly":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"A month-end summary emailed to participants recapping how they are doing in the program. Available for both referral and affiliate programs."},"commissionGenerated":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to an affiliate when they earn a new commission, for example when a customer they referred makes a purchase. Affiliate programs only."},"commissionAdjusted":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to an affiliate when one of their commissions is adjusted because of a full refund, partial refund, or chargeback. Affiliate programs only."},"payoutPending":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to an affiliate when a batch payment is started and they have a payout on the way. Affiliate programs only."},"payoutSentSuccess":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Sent to an affiliate when their payout completes successfully. Labeled \"Payout Sent Successfully\" in the Program Editor. Affiliate programs only."},"invite":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"The invitation email a participant sends to friends. Applies only when the program sends invites on the company's behalf (Design > Share > Invite, with your company as the sender). Its `useCompanyReplyTo` field sets whether replies go to your company or to the participant who sent the invite. Available for both referral and affiliate programs."},"loginLink":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"The one-time sign-in link emailed to returning participants. Applies when participant sign-in is required (in Options). Labeled \"One-Time Login Link\" in the Program Editor. This is a transactional email, so its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"paypalEmailConfirmation":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Asks a participant to set or confirm the PayPal email address where they will receive payouts. Applies when the confirm-email setting is on in the PayPal integration. This is a transactional email, so its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"taxInfoMissing":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Asks a participant to submit the tax information required before they can be paid. Sends only when the program collects tax information. Its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"taxInfoReceived":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Confirms to a participant that the tax information they submitted was received. Its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"taxInfoApproved":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Tells a participant that their tax form is complete and approved. Labeled \"Tax Form Complete\" in the Program Editor. Its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"taxInfoRejected":{"$ref":"#/components/schemas/CampaignEmailTemplate","description":"Tells a participant that their tax information was rejected and needs to be resubmitted. Its on/off toggle cannot be changed. Available for both referral and affiliate programs."},"settings":{"$ref":"#/components/schemas/CampaignEmailSettings","description":"Sender, contact, and design settings for the program's emails (the Emails > Settings sub-page)."}}},"CampaignEmailTemplate":{"type":"object","description":"A single editable email template. `subject` and `preheader` are capped at 255 characters; `body` is HTML capped at 150000 characters.","properties":{"subject":{"type":"string","maxLength":255,"description":"The email's subject line. Supports dynamic text (`{{...}}` tokens), the same as the body."},"preheader":{"type":"string","maxLength":255,"description":"The preview text shown after the subject line in most inboxes."},"body":{"type":"string","maxLength":150000,"description":"The email body as HTML. You can personalize it with dynamic text, inserting `{{...}}` tokens like `{{firstName}}` or `{{shareUrl}}` to tailor each email to its recipient. See [Guide to using dynamic text in GrowSurf emails](https://support.growsurf.com/article/213-guide-to-using-dynamic-text-in-growsurf-emails)."},"isEnabled":{"type":"boolean","description":"Whether this email is enabled. Read-only for transactional and invite emails (only promotional emails can be toggled) — sending it for those emails returns a `400`."},"useCompanyReplyTo":{"type":"boolean","description":"Only applicable to the `invite` email. When `true`, replies go to your company's reply-to address instead of the referring participant, which keeps the participant's email address private (it is never exposed to the people they invite). When `false`, replies go to the referring participant."}}},"CampaignEmailSettings":{"type":"object","description":"Email sender, contact, and design settings (the Emails → Settings sub-page).","properties":{"sender":{"description":"Sender name and reply-to settings for program emails.","$ref":"#/components/schemas/CampaignEmailSenderSettings"},"contact":{"$ref":"#/components/schemas/CampaignEmailContactSettings"},"design":{"description":"Design settings shared across program emails.","$ref":"#/components/schemas/CampaignEmailDesignSettings"}}},"CampaignEmailSenderSettings":{"type":"object","properties":{"fromName":{"type":"string","maxLength":100,"description":"The email sender name. Required; cannot be an email address."},"replyToEmail":{"type":"string","maxLength":100,"description":"The reply-to email address. Required; must be a valid email address."},"fromEmail":{"type":"string","readOnly":true,"description":"The from email address. Read-only — setting a custom from-address requires domain verification in the dashboard."}}},"CampaignEmailContactSettings":{"type":"object","description":"The physical mailing address shown in your email footers (required by anti-spam laws such as the US CAN-SPAM Act).","properties":{"companyName":{"type":"string","maxLength":255,"description":"Required."},"addressLine1":{"type":"string","maxLength":255,"description":"Required."},"addressLine2":{"description":"Second address line shown in email footers.","type":["string","null"],"maxLength":255},"city":{"type":"string","maxLength":255,"description":"Required."},"state":{"description":"State, province, or region shown in email footers.","type":["string","null"],"maxLength":255},"postalCode":{"description":"Postal or ZIP code shown in email footers.","type":["string","null"],"maxLength":100},"country":{"type":["string","null"],"maxLength":100,"description":"Free-text country (not validated as an ISO code)."}}},"CampaignEmailDesignSettings":{"type":"object","properties":{"header":{"type":["string","null"],"maxLength":150000,"description":"Email header HTML."},"footer":{"type":["string","null"],"maxLength":150000,"description":"Email footer HTML."},"unsubscribePromotional":{"type":"string","maxLength":150000,"description":"Unsubscribe footer for promotional emails. Must contain the tokens `{{unsubscribeLink}}`, `{{companyName}}`, and `{{address}}`."},"unsubscribeInvite":{"type":"string","maxLength":150000,"description":"Unsubscribe footer for invite emails. Must contain the tokens `{{unsubscribeLink}}`, `{{companyName}}`, and `{{address}}`."},"unsubscribeTransactional":{"type":"string","maxLength":150000,"description":"Footer for transactional emails. Must contain the tokens `{{companyName}}` and `{{address}}`."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/emails":{"patch":{"tags":["Campaigns"],"operationId":"updateCampaignEmails","summary":"Update campaign emails","description":"Updates a program's email configuration. Only the fields you send are changed; omitted fields are left untouched. You may only write the email templates the dashboard exposes for the program type — writing a template that is not available for the program type returns a `400`. Some fields are read-only (`settings.sender.fromEmail`, whose custom value requires dashboard domain verification).","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignEmails"}}}},"responses":{"200":{"description":"Campaign emails configuration updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignEmails"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## CAMPAIGN OPTIONS ↓

{% hint style="info" %}
Read or update a program's **Options** tab — reward/fraud approval, anti-fraud lists and toggles, referral cookie/credit windows, reCAPTCHA, payout threshold + tax settings (affiliate programs only), and notification-email settings. `PATCH` is a partial merge (send only what you want to change). Some fields are program-type specific (`requireManualRewardApproval`/`autoFulfillRewards` are referral program only; `payoutThreshold`/`taxDocumentation` are affiliate-only). `fraud.recaptcha.secretKey` is write-only (never returned), and `referralCreditWindowDays: null` means the credit never expires.
{% endhint %}

## Retrieve campaign options

> Retrieves a program's options — the same surface as the dashboard Program Editor's \*\*Options\*\* tab. Includes reward/fraud approval, anti-fraud lists + toggles, referral cookie/credit windows, reCAPTCHA, payout threshold + tax settings (affiliate only), and notification-email settings. \`fraud.recaptcha.secretKey\` is never returned.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignOptions":{"type":"object","description":"A program's options (the dashboard Program Editor's Options tab). To see the full object with every field and its current value, `GET` this resource, then `PATCH` back only the fields you want to change. Some fields are program-type specific (see the per-field notes).","properties":{"requireManualRewardApproval":{"type":"boolean","description":"Referral programs only. When `true`, each reward a participant earns is held for you to manually approve before it unlocks (and before the reward-won email is sent); when `false`, earned rewards are approved automatically. Cannot be `true` together with `autoFulfillRewards`."},"autoFulfillRewards":{"type":"boolean","description":"Referral programs only. When `true`, rewards a participant earns are automatically marked as fulfilled (issued) instead of you marking each one by hand, usually paired with an automated reward-delivery integration. Cannot be `true` together with `requireManualRewardApproval`."},"requireManualFraudApproval":{"type":"boolean","description":"When `true`, no signup is automatically blocked for fraud. Every participant is allowed to join, and any suspected fraud is flagged in your dashboard for you to review. Cannot be `true` together with `autoBlockFraud`."},"autoBlockFraud":{"type":"boolean","description":"When `true`, signups flagged as high fraud risk are automatically blocked from joining; when `false`, they are allowed in and flagged in your dashboard for review. Cannot be `true` together with `requireManualFraudApproval`."},"requireParticipantAuth":{"type":"boolean","description":"Require returning participants to authenticate. Affiliate programs require this to be `true`."},"enforceGdprCompliance":{"type":"boolean","description":"When `true`, GrowSurf stores only the minimum participant data needed to help you meet GDPR/CCPA requirements — it does not store IP addresses, browser fingerprints, or mobile instance IDs. The anti-fraud checks that rely on those signals no longer apply while this is on."},"blockPaidAdsTraffic":{"type":"boolean","description":"When `true`, referrals from visitors who arrived through paid ads (for example Google or Facebook ads) are not attributed, so the referring participant is not credited for them. Recommended so participants don't compete with you for your own paid keywords."},"referralCookieWindowDays":{"type":"integer","enum":[1,3,7,14,30,60,90,180,365,400],"description":"How long, in days, a referral-link click is remembered in the visitor's browser. If the visitor signs up within this window, the referral is credited to the participant whose link they clicked."},"referralCreditWindowDays":{"type":["integer","null"],"enum":[1,3,7,14,30,60,90,180,365,null],"description":"How long, in days, a referred friend has to complete a qualifying action for their referrer to earn credit and unlock a reward. After this window the referral no longer counts toward rewards. `null` = never expires."},"payoutThreshold":{"type":["integer","null"],"minimum":0,"description":"Affiliate programs only. Minimum payout in the program's currency minor units (`0` or `null` = no minimum). Requires `currencyISO` to be set."},"fraud":{"$ref":"#/components/schemas/CampaignOptionsFraud","description":"Anti-fraud settings — block and allow lists for emails, IPs, and countries; toggles for blocking burner emails, data-center IPs, and referrals from high-risk referrers; per-IP signup rate limits; and reCAPTCHA."},"taxDocumentation":{"$ref":"#/components/schemas/CampaignOptionsTaxDocumentation","description":"Affiliate programs only. Your company's billing details (name, address, and VAT number) used on affiliate payout invoices and for VAT handling."},"notificationEmails":{"$ref":"#/components/schemas/CampaignOptionsNotificationEmails","description":"Owner notification-email settings — who receives program notifications and which events they are notified about."}}},"CampaignOptionsFraud":{"type":"object","description":"Anti-fraud settings.","properties":{"blockedEmails":{"type":"array","items":{"type":"string","maxLength":100},"description":"Blocked emails (wildcards allowed). Max 10000. Replaces the whole list."},"blockedIps":{"type":"array","items":{"type":"string","maxLength":100},"description":"Blocked IP addresses (wildcards allowed). Max 10000."},"blockedCountries":{"type":"array","items":{"type":"string","maxLength":100},"description":"Blocked countries (ISO 3166-1 alpha-2 codes)."},"allowedEmails":{"description":"Allowed email addresses or patterns. Replaces the whole list.","type":"array","items":{"type":"string","maxLength":100}},"allowedIps":{"description":"Allowed IP addresses or patterns. Replaces the whole list.","type":"array","items":{"type":"string","maxLength":100}},"allowedCountries":{"type":"array","items":{"type":"string","maxLength":100},"description":"Allowed countries (ISO 3166-1 alpha-2 codes)."},"blockBurnerEmails":{"description":"Whether signups from burner or disposable email addresses are blocked.","type":"boolean"},"blockDataCenterIps":{"description":"Whether signups from known data-center IP addresses are blocked.","type":"boolean"},"blockHighRiskReferrers":{"description":"Whether referrals from participants marked as high fraud risk are blocked.","type":"boolean"},"autoBlockHighRiskIps":{"description":"Whether high-risk signup IP addresses are added to the blocked IP list automatically.","type":"boolean"},"maxSignupsPerIp2Min":{"description":"Maximum signups allowed from one IP address in a 2-minute window.","type":"integer","minimum":1,"maximum":100},"maxSignupsPerIp10Min":{"description":"Maximum signups allowed from one IP address in a 10-minute window.","type":"integer","minimum":1,"maximum":100},"recaptcha":{"description":"reCAPTCHA settings for signup forms.","$ref":"#/components/schemas/CampaignOptionsRecaptcha"}}},"CampaignOptionsRecaptcha":{"type":"object","properties":{"isEnabled":{"description":"Whether reCAPTCHA is enabled on signup forms.","type":"boolean"},"siteKey":{"description":"Public reCAPTCHA site key used by the signup form.","type":["string","null"],"maxLength":100},"secretKey":{"type":["string","null"],"maxLength":100,"writeOnly":true,"description":"The reCAPTCHA secret key. Write-only — never returned by GET."}}},"CampaignOptionsTaxDocumentation":{"type":"object","description":"Affiliate programs only. Your company's billing details (name, address, and VAT number) used on affiliate payout invoices and for VAT handling.","properties":{"companyName":{"description":"Legal or billing company name used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"vatNumber":{"description":"Your company VAT number, when VAT collection applies.","type":["string","null"],"maxLength":100},"addressLine1":{"description":"First line of the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"addressLine2":{"description":"Second line of the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"city":{"description":"City for the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"state":{"description":"State, province, or region for the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"postalCode":{"description":"Postal or ZIP code for the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":100},"country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country code."},"collectAffiliateVat":{"type":"boolean","description":"Requires `vatNumber` to be set."}}},"CampaignOptionsNotificationEmails":{"type":"object","description":"Owner notification-email settings.","properties":{"recipients":{"type":"array","items":{"type":"string"},"description":"Email addresses that receive program notifications."},"events":{"type":"object","description":"Per-event on/off toggles. The available events depend on the program type.","properties":{"PARTICIPANT_REACHED_A_GOAL":{"description":"Notify recipients when a participant unlocks a reward.","type":"boolean"},"NEW_PARTICIPANT_ADDED_NON_REFERRED":{"description":"Notify recipients when a participant joins without being referred.","type":"boolean"},"NEW_PARTICIPANT_ADDED_REFERRED":{"description":"Notify recipients when a participant joins through a referral link.","type":"boolean"},"CAMPAIGN_ENDED":{"description":"Notify recipients when the program ends.","type":"boolean"},"WEEKLY_PERFORMANCE_REPORT":{"description":"Send recipients the weekly performance report.","type":"boolean"},"MONTHLY_PERFORMANCE_REPORT":{"description":"Send recipients the monthly performance report.","type":"boolean"},"NEW_COMMISSION_ADDED":{"description":"Notify recipients when an affiliate earns a new commission.","type":"boolean"},"COMMISSION_ADJUSTED":{"description":"Notify recipients when an affiliate commission is adjusted.","type":"boolean"},"NEW_PAYOUT_ISSUED":{"description":"Notify recipients when an affiliate payout is issued.","type":"boolean"},"AFFILIATE_BATCH_PAYOUT_COMPLETED":{"description":"Notify recipients when an affiliate payout batch completes.","type":"boolean"},"MONTHLY_PAYOUT_REMINDER":{"description":"Send recipients the monthly affiliate payout reminder.","type":"boolean"}}}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/options":{"get":{"tags":["Campaigns"],"operationId":"retrieveCampaignOptions","summary":"Retrieve campaign options","description":"Retrieves a program's options — the same surface as the dashboard Program Editor's **Options** tab. Includes reward/fraud approval, anti-fraud lists + toggles, referral cookie/credit windows, reCAPTCHA, payout threshold + tax settings (affiliate only), and notification-email settings. `fraud.recaptcha.secretKey` is never returned.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"responses":{"200":{"description":"Campaign options returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignOptions"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update campaign options

> Updates a program's options. Only the fields you send are changed. Some fields are program-type specific (\`requireManualRewardApproval\`/\`autoFulfillRewards\` are referral-only; \`payoutThreshold\`/\`taxDocumentation\` are affiliate-only, and affiliate programs require \`requireParticipantAuth: true\`). \`fraud.recaptcha.secretKey\` is write-only. \`referralCreditWindowDays: null\` means "never expires".

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignOptions":{"type":"object","description":"A program's options (the dashboard Program Editor's Options tab). To see the full object with every field and its current value, `GET` this resource, then `PATCH` back only the fields you want to change. Some fields are program-type specific (see the per-field notes).","properties":{"requireManualRewardApproval":{"type":"boolean","description":"Referral programs only. When `true`, each reward a participant earns is held for you to manually approve before it unlocks (and before the reward-won email is sent); when `false`, earned rewards are approved automatically. Cannot be `true` together with `autoFulfillRewards`."},"autoFulfillRewards":{"type":"boolean","description":"Referral programs only. When `true`, rewards a participant earns are automatically marked as fulfilled (issued) instead of you marking each one by hand, usually paired with an automated reward-delivery integration. Cannot be `true` together with `requireManualRewardApproval`."},"requireManualFraudApproval":{"type":"boolean","description":"When `true`, no signup is automatically blocked for fraud. Every participant is allowed to join, and any suspected fraud is flagged in your dashboard for you to review. Cannot be `true` together with `autoBlockFraud`."},"autoBlockFraud":{"type":"boolean","description":"When `true`, signups flagged as high fraud risk are automatically blocked from joining; when `false`, they are allowed in and flagged in your dashboard for review. Cannot be `true` together with `requireManualFraudApproval`."},"requireParticipantAuth":{"type":"boolean","description":"Require returning participants to authenticate. Affiliate programs require this to be `true`."},"enforceGdprCompliance":{"type":"boolean","description":"When `true`, GrowSurf stores only the minimum participant data needed to help you meet GDPR/CCPA requirements — it does not store IP addresses, browser fingerprints, or mobile instance IDs. The anti-fraud checks that rely on those signals no longer apply while this is on."},"blockPaidAdsTraffic":{"type":"boolean","description":"When `true`, referrals from visitors who arrived through paid ads (for example Google or Facebook ads) are not attributed, so the referring participant is not credited for them. Recommended so participants don't compete with you for your own paid keywords."},"referralCookieWindowDays":{"type":"integer","enum":[1,3,7,14,30,60,90,180,365,400],"description":"How long, in days, a referral-link click is remembered in the visitor's browser. If the visitor signs up within this window, the referral is credited to the participant whose link they clicked."},"referralCreditWindowDays":{"type":["integer","null"],"enum":[1,3,7,14,30,60,90,180,365,null],"description":"How long, in days, a referred friend has to complete a qualifying action for their referrer to earn credit and unlock a reward. After this window the referral no longer counts toward rewards. `null` = never expires."},"payoutThreshold":{"type":["integer","null"],"minimum":0,"description":"Affiliate programs only. Minimum payout in the program's currency minor units (`0` or `null` = no minimum). Requires `currencyISO` to be set."},"fraud":{"$ref":"#/components/schemas/CampaignOptionsFraud","description":"Anti-fraud settings — block and allow lists for emails, IPs, and countries; toggles for blocking burner emails, data-center IPs, and referrals from high-risk referrers; per-IP signup rate limits; and reCAPTCHA."},"taxDocumentation":{"$ref":"#/components/schemas/CampaignOptionsTaxDocumentation","description":"Affiliate programs only. Your company's billing details (name, address, and VAT number) used on affiliate payout invoices and for VAT handling."},"notificationEmails":{"$ref":"#/components/schemas/CampaignOptionsNotificationEmails","description":"Owner notification-email settings — who receives program notifications and which events they are notified about."}}},"CampaignOptionsFraud":{"type":"object","description":"Anti-fraud settings.","properties":{"blockedEmails":{"type":"array","items":{"type":"string","maxLength":100},"description":"Blocked emails (wildcards allowed). Max 10000. Replaces the whole list."},"blockedIps":{"type":"array","items":{"type":"string","maxLength":100},"description":"Blocked IP addresses (wildcards allowed). Max 10000."},"blockedCountries":{"type":"array","items":{"type":"string","maxLength":100},"description":"Blocked countries (ISO 3166-1 alpha-2 codes)."},"allowedEmails":{"description":"Allowed email addresses or patterns. Replaces the whole list.","type":"array","items":{"type":"string","maxLength":100}},"allowedIps":{"description":"Allowed IP addresses or patterns. Replaces the whole list.","type":"array","items":{"type":"string","maxLength":100}},"allowedCountries":{"type":"array","items":{"type":"string","maxLength":100},"description":"Allowed countries (ISO 3166-1 alpha-2 codes)."},"blockBurnerEmails":{"description":"Whether signups from burner or disposable email addresses are blocked.","type":"boolean"},"blockDataCenterIps":{"description":"Whether signups from known data-center IP addresses are blocked.","type":"boolean"},"blockHighRiskReferrers":{"description":"Whether referrals from participants marked as high fraud risk are blocked.","type":"boolean"},"autoBlockHighRiskIps":{"description":"Whether high-risk signup IP addresses are added to the blocked IP list automatically.","type":"boolean"},"maxSignupsPerIp2Min":{"description":"Maximum signups allowed from one IP address in a 2-minute window.","type":"integer","minimum":1,"maximum":100},"maxSignupsPerIp10Min":{"description":"Maximum signups allowed from one IP address in a 10-minute window.","type":"integer","minimum":1,"maximum":100},"recaptcha":{"description":"reCAPTCHA settings for signup forms.","$ref":"#/components/schemas/CampaignOptionsRecaptcha"}}},"CampaignOptionsRecaptcha":{"type":"object","properties":{"isEnabled":{"description":"Whether reCAPTCHA is enabled on signup forms.","type":"boolean"},"siteKey":{"description":"Public reCAPTCHA site key used by the signup form.","type":["string","null"],"maxLength":100},"secretKey":{"type":["string","null"],"maxLength":100,"writeOnly":true,"description":"The reCAPTCHA secret key. Write-only — never returned by GET."}}},"CampaignOptionsTaxDocumentation":{"type":"object","description":"Affiliate programs only. Your company's billing details (name, address, and VAT number) used on affiliate payout invoices and for VAT handling.","properties":{"companyName":{"description":"Legal or billing company name used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"vatNumber":{"description":"Your company VAT number, when VAT collection applies.","type":["string","null"],"maxLength":100},"addressLine1":{"description":"First line of the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"addressLine2":{"description":"Second line of the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"city":{"description":"City for the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"state":{"description":"State, province, or region for the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":255},"postalCode":{"description":"Postal or ZIP code for the company address used on affiliate payout invoices.","type":["string","null"],"maxLength":100},"country":{"type":["string","null"],"description":"ISO 3166-1 alpha-2 country code."},"collectAffiliateVat":{"type":"boolean","description":"Requires `vatNumber` to be set."}}},"CampaignOptionsNotificationEmails":{"type":"object","description":"Owner notification-email settings.","properties":{"recipients":{"type":"array","items":{"type":"string"},"description":"Email addresses that receive program notifications."},"events":{"type":"object","description":"Per-event on/off toggles. The available events depend on the program type.","properties":{"PARTICIPANT_REACHED_A_GOAL":{"description":"Notify recipients when a participant unlocks a reward.","type":"boolean"},"NEW_PARTICIPANT_ADDED_NON_REFERRED":{"description":"Notify recipients when a participant joins without being referred.","type":"boolean"},"NEW_PARTICIPANT_ADDED_REFERRED":{"description":"Notify recipients when a participant joins through a referral link.","type":"boolean"},"CAMPAIGN_ENDED":{"description":"Notify recipients when the program ends.","type":"boolean"},"WEEKLY_PERFORMANCE_REPORT":{"description":"Send recipients the weekly performance report.","type":"boolean"},"MONTHLY_PERFORMANCE_REPORT":{"description":"Send recipients the monthly performance report.","type":"boolean"},"NEW_COMMISSION_ADDED":{"description":"Notify recipients when an affiliate earns a new commission.","type":"boolean"},"COMMISSION_ADJUSTED":{"description":"Notify recipients when an affiliate commission is adjusted.","type":"boolean"},"NEW_PAYOUT_ISSUED":{"description":"Notify recipients when an affiliate payout is issued.","type":"boolean"},"AFFILIATE_BATCH_PAYOUT_COMPLETED":{"description":"Notify recipients when an affiliate payout batch completes.","type":"boolean"},"MONTHLY_PAYOUT_REMINDER":{"description":"Send recipients the monthly affiliate payout reminder.","type":"boolean"}}}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/options":{"patch":{"tags":["Campaigns"],"operationId":"updateCampaignOptions","summary":"Update campaign options","description":"Updates a program's options. Only the fields you send are changed. Some fields are program-type specific (`requireManualRewardApproval`/`autoFulfillRewards` are referral-only; `payoutThreshold`/`taxDocumentation` are affiliate-only, and affiliate programs require `requireParticipantAuth: true`). `fraud.recaptcha.secretKey` is write-only. `referralCreditWindowDays: null` means \"never expires\".","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignOptions"}}}},"responses":{"200":{"description":"Campaign options updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignOptions"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## CAMPAIGN INSTALLATION ↓

{% hint style="info" %}
Read or update a program's **Installation** tab (plus Mobile SDK settings) — the referral trigger (referral programs only), signup tracking method, share URL and whitelist, custom-form signup settings, and mobile SDK settings. `PATCH` is a partial merge (send only what you want to change). `mobile.publicKey` is read-only (server-generated), and any URLs you send must include an explicit `http://` or `https://` scheme.
{% endhint %}

## Retrieve campaign installation

> Retrieves a program's installation configuration — the same surface as the dashboard Program Editor's \*\*Installation\*\* tab (plus the Mobile SDK settings). Includes the referral trigger (referral programs only), signup tracking method, share URL and whitelist, custom-form signup settings, and mobile SDK settings.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignInstallation":{"type":"object","description":"A program's installation configuration (the dashboard Program Editor's Installation tab). To see the full object with every field and its current value, `GET` this resource, then `PATCH` back only the fields you want to change. `referralTrigger` is only present and writable for referral programs.","properties":{"referralTrigger":{"type":"string","enum":["CUSTOM","ON_SIGNUP"],"description":"Referral programs only. Sets what a referred friend must do before a referral counts. `ON_SIGNUP` (\"Sign Up\") counts the referral as soon as they sign up. `CUSTOM` (\"Sign Up + Qualifying Action\") counts it only after they sign up and also complete a qualifying action you define, such as a first payment."},"signupEvent":{"type":"string","enum":["FORM_DETECTION","PROGRAMMATIC"],"description":"The signup tracking method. `FORM_DETECTION` = GrowSurf automatically detects submissions of an existing form on your site; `PROGRAMMATIC` = you add participants via the JavaScript/mobile SDKs or this API."},"shareUrl":{"type":"string","maxLength":500,"description":"The landing page referred friends are taken to when they click a participant's referral link — usually your homepage or a marketing page. Required; must include an `http(s)://` scheme and contain no `#`, `#!`, or `??`."},"useGrowSurfHostedLinks":{"type":"boolean","description":"When `true`, each participant's referral link becomes a GrowSurf-hosted link instead of a link to your own share URL, and clicks are routed by the visitor's device (Android to Google Play, iOS to your iOS attribution or App Store URL, desktop to your share URL). Mainly used by mobile apps. Applies to referral links everywhere they appear, including the SDKs, integrations, webhooks, the GrowSurf window, and participant exports."},"allowedUrls":{"type":"array","items":{"type":"string","maxLength":500},"description":"Extra domains, beyond your share URL, where the GrowSurf window and SDK are allowed to run — handy for testing on local or staging URLs. Adding an apex domain (e.g. `https://example.com`) also allows its subdomains. Each entry must include an `http(s)://` scheme. Sending this array replaces the entire list."},"signup":{"$ref":"#/components/schemas/CampaignInstallationSignup"},"mobile":{"$ref":"#/components/schemas/CampaignInstallationMobile"}}},"CampaignInstallationSignup":{"type":"object","description":"Custom signup-form settings (applies when the signup tracking method is `FORM_DETECTION`).","properties":{"isCustomForm":{"description":"Whether signups come from your own form instead of automatic form detection.","type":"boolean"},"url":{"type":["string","null"],"maxLength":500,"description":"The custom signup form URL. Must include an `http(s)://` scheme; no `#!` or `??`. Send `null` to clear."},"redirectAfterSignup":{"type":"boolean","description":"Whether the signup form redirects to another page after submission."},"redirectUrl":{"type":["string","null"],"maxLength":500,"description":"URL the signup form redirects to. Must include an `http(s)://` scheme. Send `null` to clear."},"trackInputFields":{"description":"Whether GrowSurf watches signup form fields for referral attribution.","type":"boolean"}}},"CampaignInstallationMobile":{"type":"object","description":"Settings for the GrowSurf iOS and Android mobile SDKs — enable the mobile SDK, the read-only publishable key, and the iOS/Android attribution and app-store URLs used to route mobile referral-link clicks.","properties":{"isEnabled":{"type":"boolean","description":"Whether the mobile SDK is enabled for the campaign."},"publicKey":{"type":"string","readOnly":true,"description":"The publishable mobile SDK key used by the GrowSurf iOS and Android SDKs. Read-only."},"iosAttributionUrl":{"type":["string","null"],"maxLength":500,"description":"iOS attribution URL. Must include an `http(s)://` scheme; no `#!` or `??`. Send `null` to clear."},"iosAppStoreUrl":{"description":"iOS App Store URL used for mobile referral links.","type":["string","null"],"maxLength":500},"androidPackageName":{"type":["string","null"],"maxLength":255,"description":"Android package name, e.g. `com.example.app` (pattern: `^[a-zA-Z][\\w]*(\\.[a-zA-Z][\\w]*)+$`)."},"androidAppStoreUrl":{"description":"Google Play Store URL used for mobile referral links.","type":["string","null"],"maxLength":500}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/installation":{"get":{"tags":["Campaigns"],"operationId":"retrieveCampaignInstallation","summary":"Retrieve campaign installation","description":"Retrieves a program's installation configuration — the same surface as the dashboard Program Editor's **Installation** tab (plus the Mobile SDK settings). Includes the referral trigger (referral programs only), signup tracking method, share URL and whitelist, custom-form signup settings, and mobile SDK settings.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"responses":{"200":{"description":"Campaign installation configuration returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignInstallation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update campaign installation

> Updates a program's installation configuration. Only the fields you send are changed; omitted fields are left untouched. \`referralTrigger\` is only available for referral programs. \`mobile.publicKey\` is read-only; if no key exists yet, enabling \`mobile.isEnabled\` creates one. Changing \`shareUrl\` re-resolves its redirect destinations, which may take a moment to complete. URLs must include an explicit \`http\://\` or \`https\://\` scheme.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaigns","description":"Program retrieval, listing, creation, updates, cloning, and configuration (design, emails, options, installation, and campaign rewards)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CampaignInstallation":{"type":"object","description":"A program's installation configuration (the dashboard Program Editor's Installation tab). To see the full object with every field and its current value, `GET` this resource, then `PATCH` back only the fields you want to change. `referralTrigger` is only present and writable for referral programs.","properties":{"referralTrigger":{"type":"string","enum":["CUSTOM","ON_SIGNUP"],"description":"Referral programs only. Sets what a referred friend must do before a referral counts. `ON_SIGNUP` (\"Sign Up\") counts the referral as soon as they sign up. `CUSTOM` (\"Sign Up + Qualifying Action\") counts it only after they sign up and also complete a qualifying action you define, such as a first payment."},"signupEvent":{"type":"string","enum":["FORM_DETECTION","PROGRAMMATIC"],"description":"The signup tracking method. `FORM_DETECTION` = GrowSurf automatically detects submissions of an existing form on your site; `PROGRAMMATIC` = you add participants via the JavaScript/mobile SDKs or this API."},"shareUrl":{"type":"string","maxLength":500,"description":"The landing page referred friends are taken to when they click a participant's referral link — usually your homepage or a marketing page. Required; must include an `http(s)://` scheme and contain no `#`, `#!`, or `??`."},"useGrowSurfHostedLinks":{"type":"boolean","description":"When `true`, each participant's referral link becomes a GrowSurf-hosted link instead of a link to your own share URL, and clicks are routed by the visitor's device (Android to Google Play, iOS to your iOS attribution or App Store URL, desktop to your share URL). Mainly used by mobile apps. Applies to referral links everywhere they appear, including the SDKs, integrations, webhooks, the GrowSurf window, and participant exports."},"allowedUrls":{"type":"array","items":{"type":"string","maxLength":500},"description":"Extra domains, beyond your share URL, where the GrowSurf window and SDK are allowed to run — handy for testing on local or staging URLs. Adding an apex domain (e.g. `https://example.com`) also allows its subdomains. Each entry must include an `http(s)://` scheme. Sending this array replaces the entire list."},"signup":{"$ref":"#/components/schemas/CampaignInstallationSignup"},"mobile":{"$ref":"#/components/schemas/CampaignInstallationMobile"}}},"CampaignInstallationSignup":{"type":"object","description":"Custom signup-form settings (applies when the signup tracking method is `FORM_DETECTION`).","properties":{"isCustomForm":{"description":"Whether signups come from your own form instead of automatic form detection.","type":"boolean"},"url":{"type":["string","null"],"maxLength":500,"description":"The custom signup form URL. Must include an `http(s)://` scheme; no `#!` or `??`. Send `null` to clear."},"redirectAfterSignup":{"type":"boolean","description":"Whether the signup form redirects to another page after submission."},"redirectUrl":{"type":["string","null"],"maxLength":500,"description":"URL the signup form redirects to. Must include an `http(s)://` scheme. Send `null` to clear."},"trackInputFields":{"description":"Whether GrowSurf watches signup form fields for referral attribution.","type":"boolean"}}},"CampaignInstallationMobile":{"type":"object","description":"Settings for the GrowSurf iOS and Android mobile SDKs — enable the mobile SDK, the read-only publishable key, and the iOS/Android attribution and app-store URLs used to route mobile referral-link clicks.","properties":{"isEnabled":{"type":"boolean","description":"Whether the mobile SDK is enabled for the campaign."},"publicKey":{"type":"string","readOnly":true,"description":"The publishable mobile SDK key used by the GrowSurf iOS and Android SDKs. Read-only."},"iosAttributionUrl":{"type":["string","null"],"maxLength":500,"description":"iOS attribution URL. Must include an `http(s)://` scheme; no `#!` or `??`. Send `null` to clear."},"iosAppStoreUrl":{"description":"iOS App Store URL used for mobile referral links.","type":["string","null"],"maxLength":500},"androidPackageName":{"type":["string","null"],"maxLength":255,"description":"Android package name, e.g. `com.example.app` (pattern: `^[a-zA-Z][\\w]*(\\.[a-zA-Z][\\w]*)+$`)."},"androidAppStoreUrl":{"description":"Google Play Store URL used for mobile referral links.","type":["string","null"],"maxLength":500}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/installation":{"patch":{"tags":["Campaigns"],"operationId":"updateCampaignInstallation","summary":"Update campaign installation","description":"Updates a program's installation configuration. Only the fields you send are changed; omitted fields are left untouched. `referralTrigger` is only available for referral programs. `mobile.publicKey` is read-only; if no key exists yet, enabling `mobile.isEnabled` creates one. Changing `shareUrl` re-resolves its redirect destinations, which may take a moment to complete. URLs must include an explicit `http://` or `https://` scheme.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignInstallation"}}}},"responses":{"200":{"description":"Campaign installation configuration updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignInstallation"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## CAMPAIGN WEBHOOKS ↓

{% hint style="info" %}
Manage a program's webhooks — the HTTP endpoints GrowSurf calls when program events occur. `secret` is write-only (used to sign the `GrowSurf-Signature` HMAC header, and never returned), each webhook has a persistent `id` (the program's default webhook is `primary`), and `events` is the array of event types the webhook is subscribed to. Use the test endpoint to send a live sample event to a saved webhook using its stored URL and secret.
{% endhint %}

## List webhooks

> Lists a program's webhooks (secrets are never returned).

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaign Webhooks","description":"Program webhook configuration (create, update, delete, and test webhooks)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"WebhookListResponse":{"type":"object","required":["webhooks"],"properties":{"webhooks":{"description":"Webhooks configured for the program.","type":"array","items":{"$ref":"#/components/schemas/Webhook"}}}},"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)."},"payloadUrl":{"type":"string","nullable":true,"description":"The URL that receives webhook deliveries."},"events":{"description":"Webhook events this endpoint is subscribed to.","type":"array","items":{"$ref":"#/components/schemas/WebhookEvent"}},"isEnabled":{"description":"Whether deliveries to this webhook are enabled.","type":"boolean"},"autoDisabledDueToFailures":{"type":"boolean","readOnly":true,"description":"Read-only. Whether GrowSurf auto-disabled this webhook after repeated delivery failures."},"failureCount":{"type":"integer","readOnly":true,"description":"Read-only. Consecutive delivery failures."},"lastFailureAt":{"type":"integer","format":"int64","nullable":true,"readOnly":true,"description":"Read-only. When the last delivery failure occurred, as a Unix timestamp in milliseconds."}}},"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"]},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/webhooks":{"get":{"tags":["Campaign Webhooks"],"operationId":"listWebhooks","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"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Create webhook

> Adds a webhook to the program.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaign Webhooks","description":"Program webhook configuration (create, update, delete, and test webhooks)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CreateWebhookRequest":{"type":"object","required":["payloadUrl"],"additionalProperties":false,"properties":{"payloadUrl":{"type":"string","description":"The URL that receives webhook deliveries."},"events":{"type":"array","items":{"$ref":"#/components/schemas/WebhookEvent"},"default":[],"description":"The events this webhook is subscribed to. When omitted, the webhook is created subscribed to no events."},"secret":{"type":"string","writeOnly":true,"description":"Write-only. Used to sign deliveries (the `GrowSurf-Signature` HMAC header). Never returned."},"isEnabled":{"description":"Whether the webhook should start enabled. Defaults to `true`.","type":"boolean","default":true}}},"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"]},"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)."},"payloadUrl":{"type":"string","nullable":true,"description":"The URL that receives webhook deliveries."},"events":{"description":"Webhook events this endpoint is subscribed to.","type":"array","items":{"$ref":"#/components/schemas/WebhookEvent"}},"isEnabled":{"description":"Whether deliveries to this webhook are enabled.","type":"boolean"},"autoDisabledDueToFailures":{"type":"boolean","readOnly":true,"description":"Read-only. Whether GrowSurf auto-disabled this webhook after repeated delivery failures."},"failureCount":{"type":"integer","readOnly":true,"description":"Read-only. Consecutive delivery failures."},"lastFailureAt":{"type":"integer","format":"int64","nullable":true,"readOnly":true,"description":"Read-only. When the last delivery failure occurred, as a Unix timestamp in milliseconds."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/webhooks":{"post":{"tags":["Campaign Webhooks"],"operationId":"createWebhook","summary":"Create webhook","description":"Adds a webhook to the program.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateWebhookRequest"}}}},"responses":{"200":{"description":"Webhook created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update webhook

> Updates a webhook by id.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaign Webhooks","description":"Program webhook configuration (create, update, delete, and test webhooks)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"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"}}},"schemas":{"UpdateWebhookRequest":{"type":"object","additionalProperties":false,"properties":{"payloadUrl":{"description":"New URL that receives webhook deliveries.","type":"string"},"events":{"description":"Replacement event subscription list for this webhook.","type":"array","items":{"$ref":"#/components/schemas/WebhookEvent"}},"secret":{"type":"string","writeOnly":true,"description":"Write-only."},"isEnabled":{"description":"Whether deliveries to this webhook are enabled.","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"]},"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)."},"payloadUrl":{"type":"string","nullable":true,"description":"The URL that receives webhook deliveries."},"events":{"description":"Webhook events this endpoint is subscribed to.","type":"array","items":{"$ref":"#/components/schemas/WebhookEvent"}},"isEnabled":{"description":"Whether deliveries to this webhook are enabled.","type":"boolean"},"autoDisabledDueToFailures":{"type":"boolean","readOnly":true,"description":"Read-only. Whether GrowSurf auto-disabled this webhook after repeated delivery failures."},"failureCount":{"type":"integer","readOnly":true,"description":"Read-only. Consecutive delivery failures."},"lastFailureAt":{"type":"integer","format":"int64","nullable":true,"readOnly":true,"description":"Read-only. When the last delivery failure occurred, as a Unix timestamp in milliseconds."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/webhooks/{webhookId}":{"patch":{"tags":["Campaign Webhooks"],"operationId":"updateWebhook","summary":"Update webhook","description":"Updates a webhook by id.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/WebhookId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateWebhookRequest"}}}},"responses":{"200":{"description":"Webhook updated.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Webhook"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Delete webhook

> Removes a webhook by id.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaign Webhooks","description":"Program webhook configuration (create, update, delete, and test webhooks)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"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"}}},"schemas":{"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"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/webhooks/{webhookId}":{"delete":{"tags":["Campaign Webhooks"],"operationId":"deleteWebhook","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"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Test webhook

> Sends a live test event to a webhook using its stored URL and secret.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Campaign Webhooks","description":"Program webhook configuration (create, update, delete, and test webhooks)."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"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"}}},"schemas":{"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."}}},"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"]},"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."},"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"},"statusCode":{"description":"HTTP status code returned by the webhook endpoint.","type":"integer"}}}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/webhooks/{webhookId}/test":{"post":{"tags":["Campaign Webhooks"],"operationId":"testWebhook","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"}}}},"responses":{"200":{"description":"Test result.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookTestResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## PARTICIPANTS ↓

## List participants

> Retrieves a paged list of participants in a program.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}}},"schemas":{"ParticipantListResponse":{"type":"object","required":["participants","limit","nextId"],"properties":{"participants":{"description":"Participants returned for this page.","type":"array","items":{"$ref":"#/components/schemas/Participant"}},"limit":{"description":"Maximum number of participants requested for this page.","type":"integer"},"nextId":{"description":"Participant ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]}}},"Participant":{"type":"object","description":"Detailed information about a program participant.","required":["id","referralCount","monthlyReferralCount","rank","monthlyRank","shareUrl","rewards","email"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant."},"firstName":{"type":["string","null"],"description":"The first name of the participant."},"lastName":{"type":["string","null"],"description":"The last name of the participant."},"email":{"type":"string","description":"The email of the participant."},"paypalEmailAddress":{"type":"string","format":"email","description":"The PayPal email address on file for the participant, used for affiliate or PayPal reward payouts."},"referralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made by the participant."},"monthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made this month by the participant (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made the previous month by the participant."},"rank":{"type":"integer","readOnly":true,"description":"The rank of the participant."},"monthlyRank":{"type":"integer","readOnly":true,"description":"The monthly rank of the participant. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","readOnly":true,"description":"The previous monthly rank of the participant. Not returned if the participant did not exist in your program during the previous month."},"shareUrl":{"type":"string","readOnly":true,"description":"The unique share URL of the participant."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"The date the participant was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","readOnly":true,"description":"The source of how the participant joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the participant was referred, the referrer's status in receiving the referral credit. Only applicable if the participant was referred into the program."},"referredBy":{"type":"string","description":"The ID of the referrer. Only applicable if the participant was referred into the program."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","readOnly":true,"description":"A value that represents the integrity of the participant."},"fraudReasonCode":{"type":"string","readOnly":true,"description":"The reason for the participant's `fraudRiskLevel`:\n\n- `UNIQUE_IDENTITY` — They have a unique identity (no other participant has the same identity).\n- `DUPLICATE_EMAIL` — Their email is not unique and is identical to another participant's email (e.g., gavin@hooli.com = gavin+1@hooli.com).\n- `DUPLICATE_IDENTITY` — Their identity (fingerprint or mobile instance ID) is not unique and matches another participant's.\n- `DUPLICATE_IDENTITY_EXCESSIVE` — Within a small window of time, they had an excessive number of referred participants who also shared the same IP address.\n- `EMAIL_FRAUD` — Their email was found to match another participant's when updated in a connected billing or payment integration.\n- `SIMILAR_EMAIL` — Their browser fingerprint matches another participant's and their email looks suspiciously similar.\n- `SIMILAR_FIRST_NAME` — Their browser fingerprint matches another participant's and their first name looks suspiciously similar.\n- `SIMILAR_LAST_NAME` — Their browser fingerprint matches another participant's and their last name looks suspiciously similar.\n- `REFERRAL_EMAIL_PATTERN` — (Status for a referred person) Their email matched a similar pattern to their referrer's email.\n- `REFERRAL_CHAIN_FRAUD` — (Status for a referrer or referred person) Their characteristics matched a pattern with other referrals the referrer made, indicating a fraud ring.\n- `REFERRAL_VELOCITY_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals in a short period of time.\n- `DOMAIN_CLUSTERING_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals sharing the same suspicious domain in a short period of time.\n- `MANUAL_UPDATE` — They were manually marked as a fraudster from the GrowSurf dashboard.\n- `WHITELISTED` — They were allowed to join the program because one of their properties matched an allowed value.\n- `BLACKLIST_MATCH` — They were not allowed to join the program because one of their properties matched a blocked value.\n- `BLOCKED_IP` — They were not allowed to join the program because their IP address was recently blocked by antifraud IP throttling.\n- `REFERRER_HIGH_RISK` — They were not allowed to join the program because their referrer was a high-risk fraudster."},"isWinner":{"type":"boolean","readOnly":true,"description":"`true` if the participant has earned one or more rewards."},"shareCount":{"type":"object","readOnly":true,"additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the participant has shared their referral link, keyed by channel. Standard web channels include `email`, `facebook`, and `twitter`; SDK/native-share channels include `iosNativeShare` and `androidNativeShare`."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions the participant has made."},"uniqueImpressionCount":{"type":"integer","readOnly":true,"description":"The total number of unique impressions the participant has made."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites the participant has sent."},"referrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the participant. Limited to the 100 most recently referred friends."},"monthlyReferrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the participant this month (resets at the end of the month). Limited to the 100 most recent."},"referrer":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/ParticipantReferrer"},{"type":"null"}],"description":"An object containing the participant's referrer information. Only applicable if the participant was referred into the program."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the participant, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the participant, used for anti-fraud matching."},"mobileInstanceId":{"type":["string","null"],"description":"App-install scoped mobile identifier used for anti-fraud matching when provided by native mobile apps. The official mobile SDKs generate this as a lowercase UUID. Not stored when strict GDPR/CCPA mode is enabled."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data, useful for saving additional data for the participant (e.g., `company`, `companySize`). Metadata is never used by GrowSurf and usage is optional. Metadata is returned only in REST API calls, and never in JavaScript Web API calls. See [API Guidelines](https://docs.growsurf.com/developer-tools/rest-api/api-guidelines)."},"notes":{"type":["string","null"],"description":"Internal notes about the participant, added via the [GrowSurf Dashboard](https://growsurf.com/dashboard)."},"unsubscribed":{"type":"boolean","description":"`true` if the participant has unsubscribed from program emails."},"rewards":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ParticipantReward"},"description":"A list of the rewards the participant has earned."},"vanityKeys":{"type":"array","items":{"type":"string"},"description":"The list of vanity keys that the participant has."},"unreadCommissionsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of commissions the participant has not yet viewed."},"unreadPayoutsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of payouts the participant has not yet viewed."},"isNew":{"type":"boolean","readOnly":true,"description":"Whether this participant was newly created by the request. Returned by participant creation calls; `false` when the participant already existed."},"allMatchingFraudsters":{"type":"array","readOnly":true,"items":{"type":"object","additionalProperties":true},"description":"A list of other participants flagged as matching this participant during anti-fraud checks."},"payoutSettings":{"type":"object","readOnly":true,"description":"Payout-related actions the participant must complete before a payout can be released (e.g. confirming a PayPal email or submitting a W-9/W-8 tax form). Always present; the requiredActions array is empty when no action is required.","properties":{"requiredActions":{"description":"Actions the participant must complete before payouts can be sent.","type":"array","readOnly":true,"items":{"type":"string","enum":["PAYPAL_EMAIL","TAX_INFO"]}}}}}},"ReferralSource":{"type":"string","enum":["DIRECT","PARTICIPANT"]},"ReferralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED","INVITE_SENT"]},"FraudRiskLevel":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"ParticipantReferrer":{"type":"object","description":"Summary information about a participant's referrer, returned within the `referrer` field of a `Participant`.","properties":{"id":{"type":"string","description":"The unique identifier of the referrer."},"firstName":{"type":["string","null"],"description":"The first name of the referrer."},"lastName":{"type":["string","null"],"description":"The last name of the referrer."},"email":{"type":"string","description":"The email of the referrer."},"referralCount":{"type":"integer","description":"The total number of referrals made by the referrer."},"monthlyReferralCount":{"type":"integer","description":"The total number of referrals made this month by the referrer (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","description":"The total number of referrals made the previous month by the referrer."},"rank":{"type":"integer","description":"The rank of the referrer."},"monthlyRank":{"type":"integer","description":"The monthly rank of the referrer. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","description":"The previous monthly rank of the referrer."},"shareUrl":{"type":"string","description":"The unique share URL of the referrer."},"createdAt":{"type":"integer","format":"int64","description":"The date the referrer was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","description":"The source of how the referrer joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the referrer was themselves referred, their referrer's status in receiving the referral credit."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","description":"A value that represents the integrity of the referrer."},"fraudReasonCode":{"type":"string","description":"The reason for the referrer's `fraudRiskLevel`. See `Participant.fraudReasonCode` for the list of possible values."},"isWinner":{"type":"boolean","description":"`true` if the referrer has earned one or more rewards."},"shareCount":{"type":"object","additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the referrer has shared their referral link, keyed by channel."},"impressionCount":{"type":"integer","description":"The total number of impressions the referrer has made."},"uniqueImpressionCount":{"type":"integer","description":"The total number of unique impressions the referrer has made."},"inviteCount":{"type":"integer","description":"The total number of invites the referrer has sent."},"referrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the referrer. Limited to the 100 most recent."},"monthlyReferrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the referrer this month (resets at the end of the month). Limited to the 100 most recent."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the referrer, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the referrer, used for anti-fraud matching."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data for the referrer."},"unsubscribed":{"type":"boolean","description":"`true` if the referrer has unsubscribed from program emails."}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"ParticipantReward":{"type":"object","description":"A reward that a participant has earned. This is different from a program `Reward` Object and contains information pertinent only to the participant that earned the reward.","required":["id","rewardId","status"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant's reward. This is different for every new reward the participant earns."},"rewardId":{"type":"string","readOnly":true,"description":"The ID of the program `Reward` (`CampaignReward`) that this participant has earned."},"status":{"$ref":"#/components/schemas/RewardStatus","readOnly":true,"description":"The status of the participant's reward."},"unread":{"type":"boolean","readOnly":true,"description":"`true` if the participant has not yet seen the reward in a GrowSurf window."},"approved":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been approved."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was approved for this participant (UTC milliseconds). `null` for unapproved rewards."},"fulfilledAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was fulfilled for this participant (UTC milliseconds). `null` for unapproved or unfulfilled rewards."},"isReferrer":{"type":"boolean","readOnly":true,"description":"`true` if the participant earned the reward as the referrer; `false` if they earned it as the referred friend (only applicable for double-sided reward types)."},"isAvailable":{"type":"boolean","readOnly":true,"description":"`true` if the reward is available for the participant to claim or redeem."},"isFulfilled":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been fulfilled."},"referredId":{"type":"string","readOnly":true,"description":"The ID of the friend that was referred."},"referrerId":{"type":"string","readOnly":true,"description":"The ID of the participant that made the referral."},"commissionStructure":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The commission structure associated with this reward. Present only for affiliate programs."}}},"RewardStatus":{"type":"string","enum":["PENDING","FULFILLED"]},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participants":{"get":{"tags":["Participants"],"operationId":"listParticipants","summary":"List participants","description":"Retrieves a paged list of participants in a program.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/NextId"},{"$ref":"#/components/parameters/Limit100"}],"responses":{"200":{"description":"Participants returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## List leaderboard participants

> Retrieves participants in leaderboard order for the specified leaderboard type.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}}},"schemas":{"LeaderboardType":{"type":"string","enum":["ALL_TIME","CURRENT_MONTH","PREV_MONTH","TOTAL_IMPRESSION_COUNT","UNIQUE_IMPRESSION_COUNT","BY_COMMISSIONS","BY_REVENUE","BY_REFERRALS","BY_LEADS"],"default":"ALL_TIME"},"ParticipantListResponse":{"type":"object","required":["participants","limit","nextId"],"properties":{"participants":{"description":"Participants returned for this page.","type":"array","items":{"$ref":"#/components/schemas/Participant"}},"limit":{"description":"Maximum number of participants requested for this page.","type":"integer"},"nextId":{"description":"Participant ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]}}},"Participant":{"type":"object","description":"Detailed information about a program participant.","required":["id","referralCount","monthlyReferralCount","rank","monthlyRank","shareUrl","rewards","email"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant."},"firstName":{"type":["string","null"],"description":"The first name of the participant."},"lastName":{"type":["string","null"],"description":"The last name of the participant."},"email":{"type":"string","description":"The email of the participant."},"paypalEmailAddress":{"type":"string","format":"email","description":"The PayPal email address on file for the participant, used for affiliate or PayPal reward payouts."},"referralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made by the participant."},"monthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made this month by the participant (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made the previous month by the participant."},"rank":{"type":"integer","readOnly":true,"description":"The rank of the participant."},"monthlyRank":{"type":"integer","readOnly":true,"description":"The monthly rank of the participant. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","readOnly":true,"description":"The previous monthly rank of the participant. Not returned if the participant did not exist in your program during the previous month."},"shareUrl":{"type":"string","readOnly":true,"description":"The unique share URL of the participant."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"The date the participant was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","readOnly":true,"description":"The source of how the participant joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the participant was referred, the referrer's status in receiving the referral credit. Only applicable if the participant was referred into the program."},"referredBy":{"type":"string","description":"The ID of the referrer. Only applicable if the participant was referred into the program."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","readOnly":true,"description":"A value that represents the integrity of the participant."},"fraudReasonCode":{"type":"string","readOnly":true,"description":"The reason for the participant's `fraudRiskLevel`:\n\n- `UNIQUE_IDENTITY` — They have a unique identity (no other participant has the same identity).\n- `DUPLICATE_EMAIL` — Their email is not unique and is identical to another participant's email (e.g., gavin@hooli.com = gavin+1@hooli.com).\n- `DUPLICATE_IDENTITY` — Their identity (fingerprint or mobile instance ID) is not unique and matches another participant's.\n- `DUPLICATE_IDENTITY_EXCESSIVE` — Within a small window of time, they had an excessive number of referred participants who also shared the same IP address.\n- `EMAIL_FRAUD` — Their email was found to match another participant's when updated in a connected billing or payment integration.\n- `SIMILAR_EMAIL` — Their browser fingerprint matches another participant's and their email looks suspiciously similar.\n- `SIMILAR_FIRST_NAME` — Their browser fingerprint matches another participant's and their first name looks suspiciously similar.\n- `SIMILAR_LAST_NAME` — Their browser fingerprint matches another participant's and their last name looks suspiciously similar.\n- `REFERRAL_EMAIL_PATTERN` — (Status for a referred person) Their email matched a similar pattern to their referrer's email.\n- `REFERRAL_CHAIN_FRAUD` — (Status for a referrer or referred person) Their characteristics matched a pattern with other referrals the referrer made, indicating a fraud ring.\n- `REFERRAL_VELOCITY_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals in a short period of time.\n- `DOMAIN_CLUSTERING_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals sharing the same suspicious domain in a short period of time.\n- `MANUAL_UPDATE` — They were manually marked as a fraudster from the GrowSurf dashboard.\n- `WHITELISTED` — They were allowed to join the program because one of their properties matched an allowed value.\n- `BLACKLIST_MATCH` — They were not allowed to join the program because one of their properties matched a blocked value.\n- `BLOCKED_IP` — They were not allowed to join the program because their IP address was recently blocked by antifraud IP throttling.\n- `REFERRER_HIGH_RISK` — They were not allowed to join the program because their referrer was a high-risk fraudster."},"isWinner":{"type":"boolean","readOnly":true,"description":"`true` if the participant has earned one or more rewards."},"shareCount":{"type":"object","readOnly":true,"additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the participant has shared their referral link, keyed by channel. Standard web channels include `email`, `facebook`, and `twitter`; SDK/native-share channels include `iosNativeShare` and `androidNativeShare`."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions the participant has made."},"uniqueImpressionCount":{"type":"integer","readOnly":true,"description":"The total number of unique impressions the participant has made."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites the participant has sent."},"referrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the participant. Limited to the 100 most recently referred friends."},"monthlyReferrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the participant this month (resets at the end of the month). Limited to the 100 most recent."},"referrer":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/ParticipantReferrer"},{"type":"null"}],"description":"An object containing the participant's referrer information. Only applicable if the participant was referred into the program."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the participant, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the participant, used for anti-fraud matching."},"mobileInstanceId":{"type":["string","null"],"description":"App-install scoped mobile identifier used for anti-fraud matching when provided by native mobile apps. The official mobile SDKs generate this as a lowercase UUID. Not stored when strict GDPR/CCPA mode is enabled."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data, useful for saving additional data for the participant (e.g., `company`, `companySize`). Metadata is never used by GrowSurf and usage is optional. Metadata is returned only in REST API calls, and never in JavaScript Web API calls. See [API Guidelines](https://docs.growsurf.com/developer-tools/rest-api/api-guidelines)."},"notes":{"type":["string","null"],"description":"Internal notes about the participant, added via the [GrowSurf Dashboard](https://growsurf.com/dashboard)."},"unsubscribed":{"type":"boolean","description":"`true` if the participant has unsubscribed from program emails."},"rewards":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ParticipantReward"},"description":"A list of the rewards the participant has earned."},"vanityKeys":{"type":"array","items":{"type":"string"},"description":"The list of vanity keys that the participant has."},"unreadCommissionsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of commissions the participant has not yet viewed."},"unreadPayoutsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of payouts the participant has not yet viewed."},"isNew":{"type":"boolean","readOnly":true,"description":"Whether this participant was newly created by the request. Returned by participant creation calls; `false` when the participant already existed."},"allMatchingFraudsters":{"type":"array","readOnly":true,"items":{"type":"object","additionalProperties":true},"description":"A list of other participants flagged as matching this participant during anti-fraud checks."},"payoutSettings":{"type":"object","readOnly":true,"description":"Payout-related actions the participant must complete before a payout can be released (e.g. confirming a PayPal email or submitting a W-9/W-8 tax form). Always present; the requiredActions array is empty when no action is required.","properties":{"requiredActions":{"description":"Actions the participant must complete before payouts can be sent.","type":"array","readOnly":true,"items":{"type":"string","enum":["PAYPAL_EMAIL","TAX_INFO"]}}}}}},"ReferralSource":{"type":"string","enum":["DIRECT","PARTICIPANT"]},"ReferralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED","INVITE_SENT"]},"FraudRiskLevel":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"ParticipantReferrer":{"type":"object","description":"Summary information about a participant's referrer, returned within the `referrer` field of a `Participant`.","properties":{"id":{"type":"string","description":"The unique identifier of the referrer."},"firstName":{"type":["string","null"],"description":"The first name of the referrer."},"lastName":{"type":["string","null"],"description":"The last name of the referrer."},"email":{"type":"string","description":"The email of the referrer."},"referralCount":{"type":"integer","description":"The total number of referrals made by the referrer."},"monthlyReferralCount":{"type":"integer","description":"The total number of referrals made this month by the referrer (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","description":"The total number of referrals made the previous month by the referrer."},"rank":{"type":"integer","description":"The rank of the referrer."},"monthlyRank":{"type":"integer","description":"The monthly rank of the referrer. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","description":"The previous monthly rank of the referrer."},"shareUrl":{"type":"string","description":"The unique share URL of the referrer."},"createdAt":{"type":"integer","format":"int64","description":"The date the referrer was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","description":"The source of how the referrer joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the referrer was themselves referred, their referrer's status in receiving the referral credit."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","description":"A value that represents the integrity of the referrer."},"fraudReasonCode":{"type":"string","description":"The reason for the referrer's `fraudRiskLevel`. See `Participant.fraudReasonCode` for the list of possible values."},"isWinner":{"type":"boolean","description":"`true` if the referrer has earned one or more rewards."},"shareCount":{"type":"object","additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the referrer has shared their referral link, keyed by channel."},"impressionCount":{"type":"integer","description":"The total number of impressions the referrer has made."},"uniqueImpressionCount":{"type":"integer","description":"The total number of unique impressions the referrer has made."},"inviteCount":{"type":"integer","description":"The total number of invites the referrer has sent."},"referrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the referrer. Limited to the 100 most recent."},"monthlyReferrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the referrer this month (resets at the end of the month). Limited to the 100 most recent."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the referrer, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the referrer, used for anti-fraud matching."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data for the referrer."},"unsubscribed":{"type":"boolean","description":"`true` if the referrer has unsubscribed from program emails."}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"ParticipantReward":{"type":"object","description":"A reward that a participant has earned. This is different from a program `Reward` Object and contains information pertinent only to the participant that earned the reward.","required":["id","rewardId","status"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant's reward. This is different for every new reward the participant earns."},"rewardId":{"type":"string","readOnly":true,"description":"The ID of the program `Reward` (`CampaignReward`) that this participant has earned."},"status":{"$ref":"#/components/schemas/RewardStatus","readOnly":true,"description":"The status of the participant's reward."},"unread":{"type":"boolean","readOnly":true,"description":"`true` if the participant has not yet seen the reward in a GrowSurf window."},"approved":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been approved."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was approved for this participant (UTC milliseconds). `null` for unapproved rewards."},"fulfilledAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was fulfilled for this participant (UTC milliseconds). `null` for unapproved or unfulfilled rewards."},"isReferrer":{"type":"boolean","readOnly":true,"description":"`true` if the participant earned the reward as the referrer; `false` if they earned it as the referred friend (only applicable for double-sided reward types)."},"isAvailable":{"type":"boolean","readOnly":true,"description":"`true` if the reward is available for the participant to claim or redeem."},"isFulfilled":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been fulfilled."},"referredId":{"type":"string","readOnly":true,"description":"The ID of the friend that was referred."},"referrerId":{"type":"string","readOnly":true,"description":"The ID of the participant that made the referral."},"commissionStructure":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The commission structure associated with this reward. Present only for affiliate programs."}}},"RewardStatus":{"type":"string","enum":["PENDING","FULFILLED"]},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/leaderboard":{"get":{"tags":["Participants"],"operationId":"listLeaderboard","summary":"List leaderboard participants","description":"Retrieves participants in leaderboard order for the specified leaderboard type.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/NextId"},{"$ref":"#/components/parameters/Limit100"},{"name":"isMonthly","in":"query","deprecated":true,"description":"Deprecated. Use `leaderboardType=CURRENT_MONTH` instead.","schema":{"type":"boolean","default":false}},{"name":"leaderboardType","in":"query","description":"Leaderboard ordering mode.","schema":{"$ref":"#/components/schemas/LeaderboardType"}}],"responses":{"200":{"description":"Leaderboard participants returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Add participant

> Adds a new participant to the program. If the email already exists, the existing participant is returned.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CreateParticipantRequest":{"type":"object","required":["email"],"properties":{"email":{"description":"Participant email address.","type":"string","format":"email"},"referredBy":{"type":"string","description":"Referrer participant ID or email address.","maxLength":100},"referralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED"],"description":"The referral credit status, only meaningful when `referredBy` resolves to a referrer. When omitted, it is derived from the program's referral trigger: `CREDIT_AWARDED`, `CREDIT_PENDING` (manual or custom trigger), or `CREDIT_EXPIRED` (past the credit window). Left unset when no referrer resolves."},"firstName":{"description":"Participant first name.","type":"string","maxLength":255},"lastName":{"description":"Participant last name.","type":"string","maxLength":255},"ipAddress":{"description":"Participant IP address, used for fraud checks when provided.","type":"string"},"fingerprint":{"description":"Browser fingerprint, used for fraud checks when provided.","type":"string"},"mobileInstanceId":{"type":"string","description":"Optional app-install scoped identifier for native mobile anti-fraud. Recommended for mobile participant creation and mobile participant token flows. The official mobile SDKs generate this as a lowercase UUID."},"metadata":{"$ref":"#/components/schemas/Metadata"}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"Participant":{"type":"object","description":"Detailed information about a program participant.","required":["id","referralCount","monthlyReferralCount","rank","monthlyRank","shareUrl","rewards","email"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant."},"firstName":{"type":["string","null"],"description":"The first name of the participant."},"lastName":{"type":["string","null"],"description":"The last name of the participant."},"email":{"type":"string","description":"The email of the participant."},"paypalEmailAddress":{"type":"string","format":"email","description":"The PayPal email address on file for the participant, used for affiliate or PayPal reward payouts."},"referralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made by the participant."},"monthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made this month by the participant (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made the previous month by the participant."},"rank":{"type":"integer","readOnly":true,"description":"The rank of the participant."},"monthlyRank":{"type":"integer","readOnly":true,"description":"The monthly rank of the participant. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","readOnly":true,"description":"The previous monthly rank of the participant. Not returned if the participant did not exist in your program during the previous month."},"shareUrl":{"type":"string","readOnly":true,"description":"The unique share URL of the participant."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"The date the participant was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","readOnly":true,"description":"The source of how the participant joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the participant was referred, the referrer's status in receiving the referral credit. Only applicable if the participant was referred into the program."},"referredBy":{"type":"string","description":"The ID of the referrer. Only applicable if the participant was referred into the program."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","readOnly":true,"description":"A value that represents the integrity of the participant."},"fraudReasonCode":{"type":"string","readOnly":true,"description":"The reason for the participant's `fraudRiskLevel`:\n\n- `UNIQUE_IDENTITY` — They have a unique identity (no other participant has the same identity).\n- `DUPLICATE_EMAIL` — Their email is not unique and is identical to another participant's email (e.g., gavin@hooli.com = gavin+1@hooli.com).\n- `DUPLICATE_IDENTITY` — Their identity (fingerprint or mobile instance ID) is not unique and matches another participant's.\n- `DUPLICATE_IDENTITY_EXCESSIVE` — Within a small window of time, they had an excessive number of referred participants who also shared the same IP address.\n- `EMAIL_FRAUD` — Their email was found to match another participant's when updated in a connected billing or payment integration.\n- `SIMILAR_EMAIL` — Their browser fingerprint matches another participant's and their email looks suspiciously similar.\n- `SIMILAR_FIRST_NAME` — Their browser fingerprint matches another participant's and their first name looks suspiciously similar.\n- `SIMILAR_LAST_NAME` — Their browser fingerprint matches another participant's and their last name looks suspiciously similar.\n- `REFERRAL_EMAIL_PATTERN` — (Status for a referred person) Their email matched a similar pattern to their referrer's email.\n- `REFERRAL_CHAIN_FRAUD` — (Status for a referrer or referred person) Their characteristics matched a pattern with other referrals the referrer made, indicating a fraud ring.\n- `REFERRAL_VELOCITY_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals in a short period of time.\n- `DOMAIN_CLUSTERING_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals sharing the same suspicious domain in a short period of time.\n- `MANUAL_UPDATE` — They were manually marked as a fraudster from the GrowSurf dashboard.\n- `WHITELISTED` — They were allowed to join the program because one of their properties matched an allowed value.\n- `BLACKLIST_MATCH` — They were not allowed to join the program because one of their properties matched a blocked value.\n- `BLOCKED_IP` — They were not allowed to join the program because their IP address was recently blocked by antifraud IP throttling.\n- `REFERRER_HIGH_RISK` — They were not allowed to join the program because their referrer was a high-risk fraudster."},"isWinner":{"type":"boolean","readOnly":true,"description":"`true` if the participant has earned one or more rewards."},"shareCount":{"type":"object","readOnly":true,"additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the participant has shared their referral link, keyed by channel. Standard web channels include `email`, `facebook`, and `twitter`; SDK/native-share channels include `iosNativeShare` and `androidNativeShare`."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions the participant has made."},"uniqueImpressionCount":{"type":"integer","readOnly":true,"description":"The total number of unique impressions the participant has made."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites the participant has sent."},"referrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the participant. Limited to the 100 most recently referred friends."},"monthlyReferrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the participant this month (resets at the end of the month). Limited to the 100 most recent."},"referrer":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/ParticipantReferrer"},{"type":"null"}],"description":"An object containing the participant's referrer information. Only applicable if the participant was referred into the program."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the participant, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the participant, used for anti-fraud matching."},"mobileInstanceId":{"type":["string","null"],"description":"App-install scoped mobile identifier used for anti-fraud matching when provided by native mobile apps. The official mobile SDKs generate this as a lowercase UUID. Not stored when strict GDPR/CCPA mode is enabled."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data, useful for saving additional data for the participant (e.g., `company`, `companySize`). Metadata is never used by GrowSurf and usage is optional. Metadata is returned only in REST API calls, and never in JavaScript Web API calls. See [API Guidelines](https://docs.growsurf.com/developer-tools/rest-api/api-guidelines)."},"notes":{"type":["string","null"],"description":"Internal notes about the participant, added via the [GrowSurf Dashboard](https://growsurf.com/dashboard)."},"unsubscribed":{"type":"boolean","description":"`true` if the participant has unsubscribed from program emails."},"rewards":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ParticipantReward"},"description":"A list of the rewards the participant has earned."},"vanityKeys":{"type":"array","items":{"type":"string"},"description":"The list of vanity keys that the participant has."},"unreadCommissionsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of commissions the participant has not yet viewed."},"unreadPayoutsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of payouts the participant has not yet viewed."},"isNew":{"type":"boolean","readOnly":true,"description":"Whether this participant was newly created by the request. Returned by participant creation calls; `false` when the participant already existed."},"allMatchingFraudsters":{"type":"array","readOnly":true,"items":{"type":"object","additionalProperties":true},"description":"A list of other participants flagged as matching this participant during anti-fraud checks."},"payoutSettings":{"type":"object","readOnly":true,"description":"Payout-related actions the participant must complete before a payout can be released (e.g. confirming a PayPal email or submitting a W-9/W-8 tax form). Always present; the requiredActions array is empty when no action is required.","properties":{"requiredActions":{"description":"Actions the participant must complete before payouts can be sent.","type":"array","readOnly":true,"items":{"type":"string","enum":["PAYPAL_EMAIL","TAX_INFO"]}}}}}},"ReferralSource":{"type":"string","enum":["DIRECT","PARTICIPANT"]},"ReferralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED","INVITE_SENT"]},"FraudRiskLevel":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"ParticipantReferrer":{"type":"object","description":"Summary information about a participant's referrer, returned within the `referrer` field of a `Participant`.","properties":{"id":{"type":"string","description":"The unique identifier of the referrer."},"firstName":{"type":["string","null"],"description":"The first name of the referrer."},"lastName":{"type":["string","null"],"description":"The last name of the referrer."},"email":{"type":"string","description":"The email of the referrer."},"referralCount":{"type":"integer","description":"The total number of referrals made by the referrer."},"monthlyReferralCount":{"type":"integer","description":"The total number of referrals made this month by the referrer (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","description":"The total number of referrals made the previous month by the referrer."},"rank":{"type":"integer","description":"The rank of the referrer."},"monthlyRank":{"type":"integer","description":"The monthly rank of the referrer. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","description":"The previous monthly rank of the referrer."},"shareUrl":{"type":"string","description":"The unique share URL of the referrer."},"createdAt":{"type":"integer","format":"int64","description":"The date the referrer was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","description":"The source of how the referrer joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the referrer was themselves referred, their referrer's status in receiving the referral credit."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","description":"A value that represents the integrity of the referrer."},"fraudReasonCode":{"type":"string","description":"The reason for the referrer's `fraudRiskLevel`. See `Participant.fraudReasonCode` for the list of possible values."},"isWinner":{"type":"boolean","description":"`true` if the referrer has earned one or more rewards."},"shareCount":{"type":"object","additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the referrer has shared their referral link, keyed by channel."},"impressionCount":{"type":"integer","description":"The total number of impressions the referrer has made."},"uniqueImpressionCount":{"type":"integer","description":"The total number of unique impressions the referrer has made."},"inviteCount":{"type":"integer","description":"The total number of invites the referrer has sent."},"referrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the referrer. Limited to the 100 most recent."},"monthlyReferrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the referrer this month (resets at the end of the month). Limited to the 100 most recent."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the referrer, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the referrer, used for anti-fraud matching."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data for the referrer."},"unsubscribed":{"type":"boolean","description":"`true` if the referrer has unsubscribed from program emails."}}},"ParticipantReward":{"type":"object","description":"A reward that a participant has earned. This is different from a program `Reward` Object and contains information pertinent only to the participant that earned the reward.","required":["id","rewardId","status"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant's reward. This is different for every new reward the participant earns."},"rewardId":{"type":"string","readOnly":true,"description":"The ID of the program `Reward` (`CampaignReward`) that this participant has earned."},"status":{"$ref":"#/components/schemas/RewardStatus","readOnly":true,"description":"The status of the participant's reward."},"unread":{"type":"boolean","readOnly":true,"description":"`true` if the participant has not yet seen the reward in a GrowSurf window."},"approved":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been approved."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was approved for this participant (UTC milliseconds). `null` for unapproved rewards."},"fulfilledAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was fulfilled for this participant (UTC milliseconds). `null` for unapproved or unfulfilled rewards."},"isReferrer":{"type":"boolean","readOnly":true,"description":"`true` if the participant earned the reward as the referrer; `false` if they earned it as the referred friend (only applicable for double-sided reward types)."},"isAvailable":{"type":"boolean","readOnly":true,"description":"`true` if the reward is available for the participant to claim or redeem."},"isFulfilled":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been fulfilled."},"referredId":{"type":"string","readOnly":true,"description":"The ID of the friend that was referred."},"referrerId":{"type":"string","readOnly":true,"description":"The ID of the participant that made the referral."},"commissionStructure":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The commission structure associated with this reward. Present only for affiliate programs."}}},"RewardStatus":{"type":"string","enum":["PENDING","FULFILLED"]},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}},"ParticipantBlockedError":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"fraudRiskLevel":{"description":"Fraud risk level assigned to the blocked signup.","$ref":"#/components/schemas/FraudRiskLevel"},"fraudReasonCode":{"description":"Machine-readable reason code for the fraud decision.","type":"string"},"matchedParticipantIds":{"description":"Existing participant IDs related to the fraud match.","type":"array","items":{"type":"string"}},"email":{"description":"Email address from the blocked signup attempt.","type":"string","format":"email"},"referrerId":{"description":"Referrer participant ID supplied with the blocked signup, or `null`.","type":["string","null"]},"ipAddress":{"description":"IP address supplied with the blocked signup, or `null`.","type":["string","null"]},"fingerprint":{"description":"Browser fingerprint supplied with the blocked signup, or `null`.","type":["string","null"]},"blockedAt":{"description":"When the signup was blocked.","type":"string","format":"date-time"}}}]}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"Conflict":{"description":"Conflicting duplicate request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant":{"post":{"tags":["Participants"],"operationId":"createParticipant","summary":"Add participant","description":"Adds a new participant to the program. If the email already exists, the existing participant is returned.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateParticipantRequest"}}}},"responses":{"200":{"description":"Participant returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Participant"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"description":"Participant was blocked or the endpoint is not available for this program type.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ParticipantBlockedError"},{"$ref":"#/components/schemas/Error"}]}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Retrieve participant

> Retrieves a single participant by GrowSurf participant ID or email address.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"Participant":{"type":"object","description":"Detailed information about a program participant.","required":["id","referralCount","monthlyReferralCount","rank","monthlyRank","shareUrl","rewards","email"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant."},"firstName":{"type":["string","null"],"description":"The first name of the participant."},"lastName":{"type":["string","null"],"description":"The last name of the participant."},"email":{"type":"string","description":"The email of the participant."},"paypalEmailAddress":{"type":"string","format":"email","description":"The PayPal email address on file for the participant, used for affiliate or PayPal reward payouts."},"referralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made by the participant."},"monthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made this month by the participant (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made the previous month by the participant."},"rank":{"type":"integer","readOnly":true,"description":"The rank of the participant."},"monthlyRank":{"type":"integer","readOnly":true,"description":"The monthly rank of the participant. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","readOnly":true,"description":"The previous monthly rank of the participant. Not returned if the participant did not exist in your program during the previous month."},"shareUrl":{"type":"string","readOnly":true,"description":"The unique share URL of the participant."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"The date the participant was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","readOnly":true,"description":"The source of how the participant joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the participant was referred, the referrer's status in receiving the referral credit. Only applicable if the participant was referred into the program."},"referredBy":{"type":"string","description":"The ID of the referrer. Only applicable if the participant was referred into the program."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","readOnly":true,"description":"A value that represents the integrity of the participant."},"fraudReasonCode":{"type":"string","readOnly":true,"description":"The reason for the participant's `fraudRiskLevel`:\n\n- `UNIQUE_IDENTITY` — They have a unique identity (no other participant has the same identity).\n- `DUPLICATE_EMAIL` — Their email is not unique and is identical to another participant's email (e.g., gavin@hooli.com = gavin+1@hooli.com).\n- `DUPLICATE_IDENTITY` — Their identity (fingerprint or mobile instance ID) is not unique and matches another participant's.\n- `DUPLICATE_IDENTITY_EXCESSIVE` — Within a small window of time, they had an excessive number of referred participants who also shared the same IP address.\n- `EMAIL_FRAUD` — Their email was found to match another participant's when updated in a connected billing or payment integration.\n- `SIMILAR_EMAIL` — Their browser fingerprint matches another participant's and their email looks suspiciously similar.\n- `SIMILAR_FIRST_NAME` — Their browser fingerprint matches another participant's and their first name looks suspiciously similar.\n- `SIMILAR_LAST_NAME` — Their browser fingerprint matches another participant's and their last name looks suspiciously similar.\n- `REFERRAL_EMAIL_PATTERN` — (Status for a referred person) Their email matched a similar pattern to their referrer's email.\n- `REFERRAL_CHAIN_FRAUD` — (Status for a referrer or referred person) Their characteristics matched a pattern with other referrals the referrer made, indicating a fraud ring.\n- `REFERRAL_VELOCITY_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals in a short period of time.\n- `DOMAIN_CLUSTERING_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals sharing the same suspicious domain in a short period of time.\n- `MANUAL_UPDATE` — They were manually marked as a fraudster from the GrowSurf dashboard.\n- `WHITELISTED` — They were allowed to join the program because one of their properties matched an allowed value.\n- `BLACKLIST_MATCH` — They were not allowed to join the program because one of their properties matched a blocked value.\n- `BLOCKED_IP` — They were not allowed to join the program because their IP address was recently blocked by antifraud IP throttling.\n- `REFERRER_HIGH_RISK` — They were not allowed to join the program because their referrer was a high-risk fraudster."},"isWinner":{"type":"boolean","readOnly":true,"description":"`true` if the participant has earned one or more rewards."},"shareCount":{"type":"object","readOnly":true,"additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the participant has shared their referral link, keyed by channel. Standard web channels include `email`, `facebook`, and `twitter`; SDK/native-share channels include `iosNativeShare` and `androidNativeShare`."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions the participant has made."},"uniqueImpressionCount":{"type":"integer","readOnly":true,"description":"The total number of unique impressions the participant has made."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites the participant has sent."},"referrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the participant. Limited to the 100 most recently referred friends."},"monthlyReferrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the participant this month (resets at the end of the month). Limited to the 100 most recent."},"referrer":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/ParticipantReferrer"},{"type":"null"}],"description":"An object containing the participant's referrer information. Only applicable if the participant was referred into the program."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the participant, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the participant, used for anti-fraud matching."},"mobileInstanceId":{"type":["string","null"],"description":"App-install scoped mobile identifier used for anti-fraud matching when provided by native mobile apps. The official mobile SDKs generate this as a lowercase UUID. Not stored when strict GDPR/CCPA mode is enabled."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data, useful for saving additional data for the participant (e.g., `company`, `companySize`). Metadata is never used by GrowSurf and usage is optional. Metadata is returned only in REST API calls, and never in JavaScript Web API calls. See [API Guidelines](https://docs.growsurf.com/developer-tools/rest-api/api-guidelines)."},"notes":{"type":["string","null"],"description":"Internal notes about the participant, added via the [GrowSurf Dashboard](https://growsurf.com/dashboard)."},"unsubscribed":{"type":"boolean","description":"`true` if the participant has unsubscribed from program emails."},"rewards":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ParticipantReward"},"description":"A list of the rewards the participant has earned."},"vanityKeys":{"type":"array","items":{"type":"string"},"description":"The list of vanity keys that the participant has."},"unreadCommissionsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of commissions the participant has not yet viewed."},"unreadPayoutsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of payouts the participant has not yet viewed."},"isNew":{"type":"boolean","readOnly":true,"description":"Whether this participant was newly created by the request. Returned by participant creation calls; `false` when the participant already existed."},"allMatchingFraudsters":{"type":"array","readOnly":true,"items":{"type":"object","additionalProperties":true},"description":"A list of other participants flagged as matching this participant during anti-fraud checks."},"payoutSettings":{"type":"object","readOnly":true,"description":"Payout-related actions the participant must complete before a payout can be released (e.g. confirming a PayPal email or submitting a W-9/W-8 tax form). Always present; the requiredActions array is empty when no action is required.","properties":{"requiredActions":{"description":"Actions the participant must complete before payouts can be sent.","type":"array","readOnly":true,"items":{"type":"string","enum":["PAYPAL_EMAIL","TAX_INFO"]}}}}}},"ReferralSource":{"type":"string","enum":["DIRECT","PARTICIPANT"]},"ReferralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED","INVITE_SENT"]},"FraudRiskLevel":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"ParticipantReferrer":{"type":"object","description":"Summary information about a participant's referrer, returned within the `referrer` field of a `Participant`.","properties":{"id":{"type":"string","description":"The unique identifier of the referrer."},"firstName":{"type":["string","null"],"description":"The first name of the referrer."},"lastName":{"type":["string","null"],"description":"The last name of the referrer."},"email":{"type":"string","description":"The email of the referrer."},"referralCount":{"type":"integer","description":"The total number of referrals made by the referrer."},"monthlyReferralCount":{"type":"integer","description":"The total number of referrals made this month by the referrer (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","description":"The total number of referrals made the previous month by the referrer."},"rank":{"type":"integer","description":"The rank of the referrer."},"monthlyRank":{"type":"integer","description":"The monthly rank of the referrer. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","description":"The previous monthly rank of the referrer."},"shareUrl":{"type":"string","description":"The unique share URL of the referrer."},"createdAt":{"type":"integer","format":"int64","description":"The date the referrer was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","description":"The source of how the referrer joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the referrer was themselves referred, their referrer's status in receiving the referral credit."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","description":"A value that represents the integrity of the referrer."},"fraudReasonCode":{"type":"string","description":"The reason for the referrer's `fraudRiskLevel`. See `Participant.fraudReasonCode` for the list of possible values."},"isWinner":{"type":"boolean","description":"`true` if the referrer has earned one or more rewards."},"shareCount":{"type":"object","additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the referrer has shared their referral link, keyed by channel."},"impressionCount":{"type":"integer","description":"The total number of impressions the referrer has made."},"uniqueImpressionCount":{"type":"integer","description":"The total number of unique impressions the referrer has made."},"inviteCount":{"type":"integer","description":"The total number of invites the referrer has sent."},"referrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the referrer. Limited to the 100 most recent."},"monthlyReferrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the referrer this month (resets at the end of the month). Limited to the 100 most recent."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the referrer, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the referrer, used for anti-fraud matching."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data for the referrer."},"unsubscribed":{"type":"boolean","description":"`true` if the referrer has unsubscribed from program emails."}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"ParticipantReward":{"type":"object","description":"A reward that a participant has earned. This is different from a program `Reward` Object and contains information pertinent only to the participant that earned the reward.","required":["id","rewardId","status"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant's reward. This is different for every new reward the participant earns."},"rewardId":{"type":"string","readOnly":true,"description":"The ID of the program `Reward` (`CampaignReward`) that this participant has earned."},"status":{"$ref":"#/components/schemas/RewardStatus","readOnly":true,"description":"The status of the participant's reward."},"unread":{"type":"boolean","readOnly":true,"description":"`true` if the participant has not yet seen the reward in a GrowSurf window."},"approved":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been approved."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was approved for this participant (UTC milliseconds). `null` for unapproved rewards."},"fulfilledAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was fulfilled for this participant (UTC milliseconds). `null` for unapproved or unfulfilled rewards."},"isReferrer":{"type":"boolean","readOnly":true,"description":"`true` if the participant earned the reward as the referrer; `false` if they earned it as the referred friend (only applicable for double-sided reward types)."},"isAvailable":{"type":"boolean","readOnly":true,"description":"`true` if the reward is available for the participant to claim or redeem."},"isFulfilled":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been fulfilled."},"referredId":{"type":"string","readOnly":true,"description":"The ID of the friend that was referred."},"referrerId":{"type":"string","readOnly":true,"description":"The ID of the participant that made the referral."},"commissionStructure":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The commission structure associated with this reward. Present only for affiliate programs."}}},"RewardStatus":{"type":"string","enum":["PENDING","FULFILLED"]},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}":{"get":{"tags":["Participants"],"operationId":"retrieveParticipant","summary":"Retrieve participant","description":"Retrieves a single participant by GrowSurf participant ID or email address.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"responses":{"200":{"description":"Participant returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Participant"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Update participant

> Updates a participant by GrowSurf participant ID or email address.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"UpdateParticipantRequest":{"type":"object","description":"The participant fields you can update. Any property not listed here is rejected with a `400` (you cannot update read-only fields such as counters, `origin`, or fraud state).","additionalProperties":false,"properties":{"referredBy":{"description":"Referrer participant ID or email address to assign to this participant.","type":"string","maxLength":100},"email":{"description":"Participant email address.","type":"string","format":"email"},"firstName":{"description":"Participant first name.","type":"string","maxLength":255},"lastName":{"description":"Participant last name.","type":"string","maxLength":255},"metadata":{"$ref":"#/components/schemas/Metadata"},"referralStatus":{"description":"Referral credit status for this participant.","type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED"]},"vanityKeys":{"description":"Custom referral-code aliases for this participant. Sending this array replaces the existing aliases.","type":"array","maxItems":5,"items":{"type":"string","minLength":1,"maxLength":20,"pattern":"^[A-Za-z0-9_-]+$"}},"unsubscribed":{"description":"Whether the participant is unsubscribed from program emails.","type":"boolean"},"notes":{"type":"string","maxLength":500,"description":"Freeform internal notes about the participant (internal only, never exposed to participants)."},"paypalEmail":{"type":"string","format":"email","description":"The participant's PayPal email address, used for affiliate payouts."}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"Participant":{"type":"object","description":"Detailed information about a program participant.","required":["id","referralCount","monthlyReferralCount","rank","monthlyRank","shareUrl","rewards","email"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant."},"firstName":{"type":["string","null"],"description":"The first name of the participant."},"lastName":{"type":["string","null"],"description":"The last name of the participant."},"email":{"type":"string","description":"The email of the participant."},"paypalEmailAddress":{"type":"string","format":"email","description":"The PayPal email address on file for the participant, used for affiliate or PayPal reward payouts."},"referralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made by the participant."},"monthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made this month by the participant (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made the previous month by the participant."},"rank":{"type":"integer","readOnly":true,"description":"The rank of the participant."},"monthlyRank":{"type":"integer","readOnly":true,"description":"The monthly rank of the participant. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","readOnly":true,"description":"The previous monthly rank of the participant. Not returned if the participant did not exist in your program during the previous month."},"shareUrl":{"type":"string","readOnly":true,"description":"The unique share URL of the participant."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"The date the participant was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","readOnly":true,"description":"The source of how the participant joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the participant was referred, the referrer's status in receiving the referral credit. Only applicable if the participant was referred into the program."},"referredBy":{"type":"string","description":"The ID of the referrer. Only applicable if the participant was referred into the program."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","readOnly":true,"description":"A value that represents the integrity of the participant."},"fraudReasonCode":{"type":"string","readOnly":true,"description":"The reason for the participant's `fraudRiskLevel`:\n\n- `UNIQUE_IDENTITY` — They have a unique identity (no other participant has the same identity).\n- `DUPLICATE_EMAIL` — Their email is not unique and is identical to another participant's email (e.g., gavin@hooli.com = gavin+1@hooli.com).\n- `DUPLICATE_IDENTITY` — Their identity (fingerprint or mobile instance ID) is not unique and matches another participant's.\n- `DUPLICATE_IDENTITY_EXCESSIVE` — Within a small window of time, they had an excessive number of referred participants who also shared the same IP address.\n- `EMAIL_FRAUD` — Their email was found to match another participant's when updated in a connected billing or payment integration.\n- `SIMILAR_EMAIL` — Their browser fingerprint matches another participant's and their email looks suspiciously similar.\n- `SIMILAR_FIRST_NAME` — Their browser fingerprint matches another participant's and their first name looks suspiciously similar.\n- `SIMILAR_LAST_NAME` — Their browser fingerprint matches another participant's and their last name looks suspiciously similar.\n- `REFERRAL_EMAIL_PATTERN` — (Status for a referred person) Their email matched a similar pattern to their referrer's email.\n- `REFERRAL_CHAIN_FRAUD` — (Status for a referrer or referred person) Their characteristics matched a pattern with other referrals the referrer made, indicating a fraud ring.\n- `REFERRAL_VELOCITY_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals in a short period of time.\n- `DOMAIN_CLUSTERING_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals sharing the same suspicious domain in a short period of time.\n- `MANUAL_UPDATE` — They were manually marked as a fraudster from the GrowSurf dashboard.\n- `WHITELISTED` — They were allowed to join the program because one of their properties matched an allowed value.\n- `BLACKLIST_MATCH` — They were not allowed to join the program because one of their properties matched a blocked value.\n- `BLOCKED_IP` — They were not allowed to join the program because their IP address was recently blocked by antifraud IP throttling.\n- `REFERRER_HIGH_RISK` — They were not allowed to join the program because their referrer was a high-risk fraudster."},"isWinner":{"type":"boolean","readOnly":true,"description":"`true` if the participant has earned one or more rewards."},"shareCount":{"type":"object","readOnly":true,"additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the participant has shared their referral link, keyed by channel. Standard web channels include `email`, `facebook`, and `twitter`; SDK/native-share channels include `iosNativeShare` and `androidNativeShare`."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions the participant has made."},"uniqueImpressionCount":{"type":"integer","readOnly":true,"description":"The total number of unique impressions the participant has made."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites the participant has sent."},"referrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the participant. Limited to the 100 most recently referred friends."},"monthlyReferrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the participant this month (resets at the end of the month). Limited to the 100 most recent."},"referrer":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/ParticipantReferrer"},{"type":"null"}],"description":"An object containing the participant's referrer information. Only applicable if the participant was referred into the program."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the participant, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the participant, used for anti-fraud matching."},"mobileInstanceId":{"type":["string","null"],"description":"App-install scoped mobile identifier used for anti-fraud matching when provided by native mobile apps. The official mobile SDKs generate this as a lowercase UUID. Not stored when strict GDPR/CCPA mode is enabled."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data, useful for saving additional data for the participant (e.g., `company`, `companySize`). Metadata is never used by GrowSurf and usage is optional. Metadata is returned only in REST API calls, and never in JavaScript Web API calls. See [API Guidelines](https://docs.growsurf.com/developer-tools/rest-api/api-guidelines)."},"notes":{"type":["string","null"],"description":"Internal notes about the participant, added via the [GrowSurf Dashboard](https://growsurf.com/dashboard)."},"unsubscribed":{"type":"boolean","description":"`true` if the participant has unsubscribed from program emails."},"rewards":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ParticipantReward"},"description":"A list of the rewards the participant has earned."},"vanityKeys":{"type":"array","items":{"type":"string"},"description":"The list of vanity keys that the participant has."},"unreadCommissionsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of commissions the participant has not yet viewed."},"unreadPayoutsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of payouts the participant has not yet viewed."},"isNew":{"type":"boolean","readOnly":true,"description":"Whether this participant was newly created by the request. Returned by participant creation calls; `false` when the participant already existed."},"allMatchingFraudsters":{"type":"array","readOnly":true,"items":{"type":"object","additionalProperties":true},"description":"A list of other participants flagged as matching this participant during anti-fraud checks."},"payoutSettings":{"type":"object","readOnly":true,"description":"Payout-related actions the participant must complete before a payout can be released (e.g. confirming a PayPal email or submitting a W-9/W-8 tax form). Always present; the requiredActions array is empty when no action is required.","properties":{"requiredActions":{"description":"Actions the participant must complete before payouts can be sent.","type":"array","readOnly":true,"items":{"type":"string","enum":["PAYPAL_EMAIL","TAX_INFO"]}}}}}},"ReferralSource":{"type":"string","enum":["DIRECT","PARTICIPANT"]},"ReferralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED","INVITE_SENT"]},"FraudRiskLevel":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"ParticipantReferrer":{"type":"object","description":"Summary information about a participant's referrer, returned within the `referrer` field of a `Participant`.","properties":{"id":{"type":"string","description":"The unique identifier of the referrer."},"firstName":{"type":["string","null"],"description":"The first name of the referrer."},"lastName":{"type":["string","null"],"description":"The last name of the referrer."},"email":{"type":"string","description":"The email of the referrer."},"referralCount":{"type":"integer","description":"The total number of referrals made by the referrer."},"monthlyReferralCount":{"type":"integer","description":"The total number of referrals made this month by the referrer (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","description":"The total number of referrals made the previous month by the referrer."},"rank":{"type":"integer","description":"The rank of the referrer."},"monthlyRank":{"type":"integer","description":"The monthly rank of the referrer. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","description":"The previous monthly rank of the referrer."},"shareUrl":{"type":"string","description":"The unique share URL of the referrer."},"createdAt":{"type":"integer","format":"int64","description":"The date the referrer was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","description":"The source of how the referrer joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the referrer was themselves referred, their referrer's status in receiving the referral credit."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","description":"A value that represents the integrity of the referrer."},"fraudReasonCode":{"type":"string","description":"The reason for the referrer's `fraudRiskLevel`. See `Participant.fraudReasonCode` for the list of possible values."},"isWinner":{"type":"boolean","description":"`true` if the referrer has earned one or more rewards."},"shareCount":{"type":"object","additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the referrer has shared their referral link, keyed by channel."},"impressionCount":{"type":"integer","description":"The total number of impressions the referrer has made."},"uniqueImpressionCount":{"type":"integer","description":"The total number of unique impressions the referrer has made."},"inviteCount":{"type":"integer","description":"The total number of invites the referrer has sent."},"referrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the referrer. Limited to the 100 most recent."},"monthlyReferrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the referrer this month (resets at the end of the month). Limited to the 100 most recent."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the referrer, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the referrer, used for anti-fraud matching."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data for the referrer."},"unsubscribed":{"type":"boolean","description":"`true` if the referrer has unsubscribed from program emails."}}},"ParticipantReward":{"type":"object","description":"A reward that a participant has earned. This is different from a program `Reward` Object and contains information pertinent only to the participant that earned the reward.","required":["id","rewardId","status"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant's reward. This is different for every new reward the participant earns."},"rewardId":{"type":"string","readOnly":true,"description":"The ID of the program `Reward` (`CampaignReward`) that this participant has earned."},"status":{"$ref":"#/components/schemas/RewardStatus","readOnly":true,"description":"The status of the participant's reward."},"unread":{"type":"boolean","readOnly":true,"description":"`true` if the participant has not yet seen the reward in a GrowSurf window."},"approved":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been approved."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was approved for this participant (UTC milliseconds). `null` for unapproved rewards."},"fulfilledAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was fulfilled for this participant (UTC milliseconds). `null` for unapproved or unfulfilled rewards."},"isReferrer":{"type":"boolean","readOnly":true,"description":"`true` if the participant earned the reward as the referrer; `false` if they earned it as the referred friend (only applicable for double-sided reward types)."},"isAvailable":{"type":"boolean","readOnly":true,"description":"`true` if the reward is available for the participant to claim or redeem."},"isFulfilled":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been fulfilled."},"referredId":{"type":"string","readOnly":true,"description":"The ID of the friend that was referred."},"referrerId":{"type":"string","readOnly":true,"description":"The ID of the participant that made the referral."},"commissionStructure":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The commission structure associated with this reward. Present only for affiliate programs."}}},"RewardStatus":{"type":"string","enum":["PENDING","FULFILLED"]},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflicting duplicate request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}":{"post":{"tags":["Participants"],"operationId":"updateParticipant","summary":"Update participant","description":"Updates a participant by GrowSurf participant ID or email address.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateParticipantRequest"}}}},"responses":{"200":{"description":"Updated participant returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Participant"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Delete participant

> Removes a participant by GrowSurf participant ID or email address.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"responses":{"Success":{"description":"Success response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}},"schemas":{"SuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"description":"Whether the request succeeded.","type":"boolean"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}":{"delete":{"tags":["Participants"],"operationId":"deleteParticipant","summary":"Delete participant","description":"Removes a participant by GrowSurf participant ID or email address.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"responses":{"200":{"$ref":"#/components/responses/Success"},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Bulk delete participants

> Deletes a list of participants from a program in one request. Each entry in \`participants\` is a GrowSurf participant ID or an email address (mixed lists are allowed). Up to \`200\` entries per request — chunk larger lists across multiple calls. The response reports a per-row \`status\` for every submitted entry, so a \`200\` can include rows that were \`NOT\_FOUND\` or failed. Deletion is permanent and removes the participants' referrals, rewards, commissions, and payout records.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"BulkDeleteParticipantsRequest":{"type":"object","required":["participants"],"additionalProperties":false,"properties":{"participants":{"type":"array","minItems":1,"maxItems":200,"description":"GrowSurf participant IDs and/or email addresses to delete. Mixed entries are allowed.","items":{"type":"string","minLength":1}}}},"BulkDeleteParticipantsResponse":{"type":"object","required":["summary","results"],"properties":{"summary":{"description":"Counts of submitted, deleted, not-found, duplicate, and failed entries.","$ref":"#/components/schemas/BulkDeleteParticipantsSummary"},"results":{"type":"array","description":"One entry per submitted identifier, in the same order as the request.","items":{"$ref":"#/components/schemas/BulkDeleteParticipantResult"}}}},"BulkDeleteParticipantsSummary":{"type":"object","required":["total","deletedCount","notFoundCount","duplicateCount","errorCount"],"properties":{"total":{"type":"integer","description":"Number of entries submitted in this request."},"deletedCount":{"type":"integer","description":"Entries that resolved to a participant and were deleted."},"notFoundCount":{"type":"integer","description":"Entries that did not match any participant."},"duplicateCount":{"type":"integer","description":"Entries that resolved to the same participant as an earlier entry."},"errorCount":{"type":"integer","description":"Entries that failed to look up or delete."}}},"BulkDeleteParticipantResult":{"type":"object","required":["index","identifier","status"],"properties":{"index":{"type":"integer","description":"Zero-based position of this entry in the submitted `participants` array."},"identifier":{"type":"string","description":"The submitted participant ID or email address, echoed back as received."},"status":{"$ref":"#/components/schemas/BulkDeleteParticipantStatus"},"participantId":{"type":"string","description":"The resolved GrowSurf participant ID. Present when the entry resolved to a participant."},"email":{"type":"string","format":"email","description":"The resolved participant's email address. Present on `DELETED` rows."},"message":{"type":"string","description":"Human-readable detail for `NOT_FOUND`, `DUPLICATE`, and `ERROR` rows."}}},"BulkDeleteParticipantStatus":{"type":"string","enum":["DELETED","NOT_FOUND","DUPLICATE","ERROR"],"description":"Per-row outcome. `DELETED` — the participant was resolved and removed. `NOT_FOUND` — no participant matches the ID or email. `DUPLICATE` — the entry resolves to the same participant as an earlier entry in the same request. `ERROR` — the lookup or deletion failed for this row."},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participants/bulk-delete":{"post":{"tags":["Participants"],"operationId":"bulkDeleteParticipants","summary":"Bulk delete participants","description":"Deletes a list of participants from a program in one request. Each entry in `participants` is a GrowSurf participant ID or an email address (mixed lists are allowed). Up to `200` entries per request — chunk larger lists across multiple calls. The response reports a per-row `status` for every submitted entry, so a `200` can include rows that were `NOT_FOUND` or failed. Deletion is permanent and removes the participants' referrals, rewards, commissions, and payout records.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDeleteParticipantsRequest"}}}},"responses":{"200":{"description":"Per-row outcomes returned (individual rows may still be `NOT_FOUND`, `DUPLICATE`, or `ERROR`).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BulkDeleteParticipantsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Trigger referral

> Triggers referral credit for an existing referred participant by GrowSurf participant ID or email address. Optionally pass \`delayInDays\` to hold the credit for a number of days before it is awarded (for example, to cover your own refund window). A delayed trigger can be cancelled before it is awarded with the Cancel delayed referral trigger request (DELETE on this same path).

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"TriggerReferralRequest":{"type":"object","description":"Optional body for a referral trigger. Omit it (or send an empty object) to award credit immediately.","properties":{"delayInDays":{"type":"integer","minimum":1,"maximum":90,"description":"Number of whole days to hold referral credit before it is awarded. Useful for honoring a refund window before crediting a referrer. Omit this field to award credit immediately. The credit is awarded automatically once the delay elapses, and can be cancelled before then with the Cancel delayed referral trigger request."}}},"ReferralTriggerResponse":{"type":"object","required":["success"],"properties":{"success":{"description":"Whether referral credit was awarded or scheduled.","type":"boolean"},"message":{"description":"Human-readable result message, present when credit was not awarded immediately or when a delayed trigger was scheduled.","type":"string"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/ref":{"post":{"tags":["Participants"],"operationId":"triggerReferral","summary":"Trigger referral","description":"Triggers referral credit for an existing referred participant by GrowSurf participant ID or email address. Optionally pass `delayInDays` to hold the credit for a number of days before it is awarded (for example, to cover your own refund window). A delayed trigger can be cancelled before it is awarded with the Cancel delayed referral trigger request (DELETE on this same path).","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerReferralRequest"}}}},"responses":{"200":{"description":"Referral trigger result returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralTriggerResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Cancel delayed referral trigger

> Cancels a pending delayed referral trigger for a participant (the companion to a delayed Trigger referral request). Use this to undo a scheduled referral credit before it is awarded, for example when a refund occurs inside your refund window. If the participant has no pending delayed trigger, \`success\` is returned as \`false\`.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"ReferralTriggerResponse":{"type":"object","required":["success"],"properties":{"success":{"description":"Whether referral credit was awarded or scheduled.","type":"boolean"},"message":{"description":"Human-readable result message, present when credit was not awarded immediately or when a delayed trigger was scheduled.","type":"string"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/ref":{"delete":{"tags":["Participants"],"operationId":"cancelDelayedReferral","summary":"Cancel delayed referral trigger","description":"Cancels a pending delayed referral trigger for a participant (the companion to a delayed Trigger referral request). Use this to undo a scheduled referral credit before it is awarded, for example when a refund occurs inside your refund window. If the participant has no pending delayed trigger, `success` is returned as `false`.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"responses":{"200":{"description":"Delayed referral trigger cancellation result returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralTriggerResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Create mobile participant token

> Creates or returns a participant using the same input behavior as Add Participant, then returns a participant-scoped token for GrowSurf mobile SDK participant endpoints. Use this endpoint from your backend after your mobile app authenticates a signed-in user. The program must have mobile SDK access enabled.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}}},"schemas":{"CreateParticipantRequest":{"type":"object","required":["email"],"properties":{"email":{"description":"Participant email address.","type":"string","format":"email"},"referredBy":{"type":"string","description":"Referrer participant ID or email address.","maxLength":100},"referralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED"],"description":"The referral credit status, only meaningful when `referredBy` resolves to a referrer. When omitted, it is derived from the program's referral trigger: `CREDIT_AWARDED`, `CREDIT_PENDING` (manual or custom trigger), or `CREDIT_EXPIRED` (past the credit window). Left unset when no referrer resolves."},"firstName":{"description":"Participant first name.","type":"string","maxLength":255},"lastName":{"description":"Participant last name.","type":"string","maxLength":255},"ipAddress":{"description":"Participant IP address, used for fraud checks when provided.","type":"string"},"fingerprint":{"description":"Browser fingerprint, used for fraud checks when provided.","type":"string"},"mobileInstanceId":{"type":"string","description":"Optional app-install scoped identifier for native mobile anti-fraud. Recommended for mobile participant creation and mobile participant token flows. The official mobile SDKs generate this as a lowercase UUID."},"metadata":{"$ref":"#/components/schemas/Metadata"}}},"Metadata":{"type":"object","description":"Shallow custom metadata object.","additionalProperties":true},"MobileParticipantTokenResponse":{"type":"object","required":["participantToken","expiresIn","participant","isNew"],"properties":{"participantToken":{"type":"string","description":"Participant-scoped bearer token for GrowSurf mobile SDK participant endpoints."},"expiresIn":{"type":"integer","description":"Token lifetime in seconds."},"participant":{"$ref":"#/components/schemas/Participant"},"isNew":{"type":"boolean","description":"Whether this request created a new participant. Returns false when the participant already existed."}}},"Participant":{"type":"object","description":"Detailed information about a program participant.","required":["id","referralCount","monthlyReferralCount","rank","monthlyRank","shareUrl","rewards","email"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant."},"firstName":{"type":["string","null"],"description":"The first name of the participant."},"lastName":{"type":["string","null"],"description":"The last name of the participant."},"email":{"type":"string","description":"The email of the participant."},"paypalEmailAddress":{"type":"string","format":"email","description":"The PayPal email address on file for the participant, used for affiliate or PayPal reward payouts."},"referralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made by the participant."},"monthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made this month by the participant (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","readOnly":true,"description":"The total number of referrals made the previous month by the participant."},"rank":{"type":"integer","readOnly":true,"description":"The rank of the participant."},"monthlyRank":{"type":"integer","readOnly":true,"description":"The monthly rank of the participant. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","readOnly":true,"description":"The previous monthly rank of the participant. Not returned if the participant did not exist in your program during the previous month."},"shareUrl":{"type":"string","readOnly":true,"description":"The unique share URL of the participant."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"The date the participant was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","readOnly":true,"description":"The source of how the participant joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the participant was referred, the referrer's status in receiving the referral credit. Only applicable if the participant was referred into the program."},"referredBy":{"type":"string","description":"The ID of the referrer. Only applicable if the participant was referred into the program."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","readOnly":true,"description":"A value that represents the integrity of the participant."},"fraudReasonCode":{"type":"string","readOnly":true,"description":"The reason for the participant's `fraudRiskLevel`:\n\n- `UNIQUE_IDENTITY` — They have a unique identity (no other participant has the same identity).\n- `DUPLICATE_EMAIL` — Their email is not unique and is identical to another participant's email (e.g., gavin@hooli.com = gavin+1@hooli.com).\n- `DUPLICATE_IDENTITY` — Their identity (fingerprint or mobile instance ID) is not unique and matches another participant's.\n- `DUPLICATE_IDENTITY_EXCESSIVE` — Within a small window of time, they had an excessive number of referred participants who also shared the same IP address.\n- `EMAIL_FRAUD` — Their email was found to match another participant's when updated in a connected billing or payment integration.\n- `SIMILAR_EMAIL` — Their browser fingerprint matches another participant's and their email looks suspiciously similar.\n- `SIMILAR_FIRST_NAME` — Their browser fingerprint matches another participant's and their first name looks suspiciously similar.\n- `SIMILAR_LAST_NAME` — Their browser fingerprint matches another participant's and their last name looks suspiciously similar.\n- `REFERRAL_EMAIL_PATTERN` — (Status for a referred person) Their email matched a similar pattern to their referrer's email.\n- `REFERRAL_CHAIN_FRAUD` — (Status for a referrer or referred person) Their characteristics matched a pattern with other referrals the referrer made, indicating a fraud ring.\n- `REFERRAL_VELOCITY_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals in a short period of time.\n- `DOMAIN_CLUSTERING_FRAUD` — (Status for a referrer or referred person) The referrer made an excessive number of referrals sharing the same suspicious domain in a short period of time.\n- `MANUAL_UPDATE` — They were manually marked as a fraudster from the GrowSurf dashboard.\n- `WHITELISTED` — They were allowed to join the program because one of their properties matched an allowed value.\n- `BLACKLIST_MATCH` — They were not allowed to join the program because one of their properties matched a blocked value.\n- `BLOCKED_IP` — They were not allowed to join the program because their IP address was recently blocked by antifraud IP throttling.\n- `REFERRER_HIGH_RISK` — They were not allowed to join the program because their referrer was a high-risk fraudster."},"isWinner":{"type":"boolean","readOnly":true,"description":"`true` if the participant has earned one or more rewards."},"shareCount":{"type":"object","readOnly":true,"additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the participant has shared their referral link, keyed by channel. Standard web channels include `email`, `facebook`, and `twitter`; SDK/native-share channels include `iosNativeShare` and `androidNativeShare`."},"impressionCount":{"type":"integer","readOnly":true,"description":"The total number of impressions the participant has made."},"uniqueImpressionCount":{"type":"integer","readOnly":true,"description":"The total number of unique impressions the participant has made."},"inviteCount":{"type":"integer","readOnly":true,"description":"The total number of invites the participant has sent."},"referrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the participant. Limited to the 100 most recently referred friends."},"monthlyReferrals":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the participant this month (resets at the end of the month). Limited to the 100 most recent."},"referrer":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/ParticipantReferrer"},{"type":"null"}],"description":"An object containing the participant's referrer information. Only applicable if the participant was referred into the program."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the participant, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the participant, used for anti-fraud matching."},"mobileInstanceId":{"type":["string","null"],"description":"App-install scoped mobile identifier used for anti-fraud matching when provided by native mobile apps. The official mobile SDKs generate this as a lowercase UUID. Not stored when strict GDPR/CCPA mode is enabled."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data, useful for saving additional data for the participant (e.g., `company`, `companySize`). Metadata is never used by GrowSurf and usage is optional. Metadata is returned only in REST API calls, and never in JavaScript Web API calls. See [API Guidelines](https://docs.growsurf.com/developer-tools/rest-api/api-guidelines)."},"notes":{"type":["string","null"],"description":"Internal notes about the participant, added via the [GrowSurf Dashboard](https://growsurf.com/dashboard)."},"unsubscribed":{"type":"boolean","description":"`true` if the participant has unsubscribed from program emails."},"rewards":{"type":"array","readOnly":true,"items":{"$ref":"#/components/schemas/ParticipantReward"},"description":"A list of the rewards the participant has earned."},"vanityKeys":{"type":"array","items":{"type":"string"},"description":"The list of vanity keys that the participant has."},"unreadCommissionsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of commissions the participant has not yet viewed."},"unreadPayoutsCount":{"type":"integer","readOnly":true,"description":"Affiliate programs only. The number of payouts the participant has not yet viewed."},"isNew":{"type":"boolean","readOnly":true,"description":"Whether this participant was newly created by the request. Returned by participant creation calls; `false` when the participant already existed."},"allMatchingFraudsters":{"type":"array","readOnly":true,"items":{"type":"object","additionalProperties":true},"description":"A list of other participants flagged as matching this participant during anti-fraud checks."},"payoutSettings":{"type":"object","readOnly":true,"description":"Payout-related actions the participant must complete before a payout can be released (e.g. confirming a PayPal email or submitting a W-9/W-8 tax form). Always present; the requiredActions array is empty when no action is required.","properties":{"requiredActions":{"description":"Actions the participant must complete before payouts can be sent.","type":"array","readOnly":true,"items":{"type":"string","enum":["PAYPAL_EMAIL","TAX_INFO"]}}}}}},"ReferralSource":{"type":"string","enum":["DIRECT","PARTICIPANT"]},"ReferralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED","INVITE_SENT"]},"FraudRiskLevel":{"type":"string","enum":["LOW","MEDIUM","HIGH"]},"ParticipantReferrer":{"type":"object","description":"Summary information about a participant's referrer, returned within the `referrer` field of a `Participant`.","properties":{"id":{"type":"string","description":"The unique identifier of the referrer."},"firstName":{"type":["string","null"],"description":"The first name of the referrer."},"lastName":{"type":["string","null"],"description":"The last name of the referrer."},"email":{"type":"string","description":"The email of the referrer."},"referralCount":{"type":"integer","description":"The total number of referrals made by the referrer."},"monthlyReferralCount":{"type":"integer","description":"The total number of referrals made this month by the referrer (resets at the end of the month)."},"prevMonthlyReferralCount":{"type":"integer","description":"The total number of referrals made the previous month by the referrer."},"rank":{"type":"integer","description":"The rank of the referrer."},"monthlyRank":{"type":"integer","description":"The monthly rank of the referrer. This rank resets to 0 at the end of each month."},"prevMonthlyRank":{"type":"integer","description":"The previous monthly rank of the referrer."},"shareUrl":{"type":"string","description":"The unique share URL of the referrer."},"createdAt":{"type":"integer","format":"int64","description":"The date the referrer was added to the program (UTC milliseconds)."},"referralSource":{"$ref":"#/components/schemas/ReferralSource","description":"The source of how the referrer joined the program."},"referralStatus":{"$ref":"#/components/schemas/ReferralStatus","description":"If the referrer was themselves referred, their referrer's status in receiving the referral credit."},"fraudRiskLevel":{"$ref":"#/components/schemas/FraudRiskLevel","description":"A value that represents the integrity of the referrer."},"fraudReasonCode":{"type":"string","description":"The reason for the referrer's `fraudRiskLevel`. See `Participant.fraudReasonCode` for the list of possible values."},"isWinner":{"type":"boolean","description":"`true` if the referrer has earned one or more rewards."},"shareCount":{"type":"object","additionalProperties":{"type":"integer"},"description":"An object containing counts of how many times the referrer has shared their referral link, keyed by channel."},"impressionCount":{"type":"integer","description":"The total number of impressions the referrer has made."},"uniqueImpressionCount":{"type":"integer","description":"The total number of unique impressions the referrer has made."},"inviteCount":{"type":"integer","description":"The total number of invites the referrer has sent."},"referrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs who were successfully referred by the referrer. Limited to the 100 most recent."},"monthlyReferrals":{"type":"array","items":{"type":"string"},"description":"A list of `Participant` IDs successfully referred by the referrer this month (resets at the end of the month). Limited to the 100 most recent."},"ipAddress":{"type":["string","null"],"description":"The IP address associated with the referrer, used for anti-fraud matching."},"fingerprint":{"type":["string","null"],"description":"The browser fingerprint associated with the referrer, used for anti-fraud matching."},"metadata":{"$ref":"#/components/schemas/Metadata","description":"An object containing any custom key-value data for the referrer."},"unsubscribed":{"type":"boolean","description":"`true` if the referrer has unsubscribed from program emails."}}},"ParticipantReward":{"type":"object","description":"A reward that a participant has earned. This is different from a program `Reward` Object and contains information pertinent only to the participant that earned the reward.","required":["id","rewardId","status"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant's reward. This is different for every new reward the participant earns."},"rewardId":{"type":"string","readOnly":true,"description":"The ID of the program `Reward` (`CampaignReward`) that this participant has earned."},"status":{"$ref":"#/components/schemas/RewardStatus","readOnly":true,"description":"The status of the participant's reward."},"unread":{"type":"boolean","readOnly":true,"description":"`true` if the participant has not yet seen the reward in a GrowSurf window."},"approved":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been approved."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was approved for this participant (UTC milliseconds). `null` for unapproved rewards."},"fulfilledAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was fulfilled for this participant (UTC milliseconds). `null` for unapproved or unfulfilled rewards."},"isReferrer":{"type":"boolean","readOnly":true,"description":"`true` if the participant earned the reward as the referrer; `false` if they earned it as the referred friend (only applicable for double-sided reward types)."},"isAvailable":{"type":"boolean","readOnly":true,"description":"`true` if the reward is available for the participant to claim or redeem."},"isFulfilled":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been fulfilled."},"referredId":{"type":"string","readOnly":true,"description":"The ID of the friend that was referred."},"referrerId":{"type":"string","readOnly":true,"description":"The ID of the participant that made the referral."},"commissionStructure":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The commission structure associated with this reward. Present only for affiliate programs."}}},"RewardStatus":{"type":"string","enum":["PENDING","FULFILLED"]},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}},"ParticipantBlockedError":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"fraudRiskLevel":{"description":"Fraud risk level assigned to the blocked signup.","$ref":"#/components/schemas/FraudRiskLevel"},"fraudReasonCode":{"description":"Machine-readable reason code for the fraud decision.","type":"string"},"matchedParticipantIds":{"description":"Existing participant IDs related to the fraud match.","type":"array","items":{"type":"string"}},"email":{"description":"Email address from the blocked signup attempt.","type":"string","format":"email"},"referrerId":{"description":"Referrer participant ID supplied with the blocked signup, or `null`.","type":["string","null"]},"ipAddress":{"description":"IP address supplied with the blocked signup, or `null`.","type":["string","null"]},"fingerprint":{"description":"Browser fingerprint supplied with the blocked signup, or `null`.","type":["string","null"]},"blockedAt":{"description":"When the signup was blocked.","type":"string","format":"date-time"}}}]}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"Conflict":{"description":"Conflicting duplicate request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/mobile-participant-token":{"post":{"tags":["Participants"],"operationId":"createMobileParticipantToken","summary":"Create mobile participant token","description":"Creates or returns a participant using the same input behavior as Add Participant, then returns a participant-scoped token for GrowSurf mobile SDK participant endpoints. Use this endpoint from your backend after your mobile app authenticates a signed-in user. The program must have mobile SDK access enabled.","parameters":[{"$ref":"#/components/parameters/CampaignId"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateParticipantRequest"}}}},"responses":{"200":{"description":"Participant and mobile participant token returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MobileParticipantTokenResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"description":"Participant was blocked or the endpoint is not available for this program type.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ParticipantBlockedError"},{"$ref":"#/components/schemas/Error"}]}}}},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Email participant

> Sends an email to a participant. Provide EITHER \`emailType\` to trigger one of the program's configured email templates, OR \`subject\` + \`body\` for a free-form email. Free-form emails are sent with the same compliance handling (company name, postal address, and an unsubscribe link are added automatically, and unsubscribed participants are suppressed). Sending requires the team to be verified by GrowSurf. Requires a \*\*verified custom email domain\*\* on the program (which can be completed in \*Campaign Editor > 3. Emails > Email Settings\*). Returns \`400\` until one is verified. The email is accepted for delivery.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"EmailParticipantRequest":{"type":"object","additionalProperties":false,"description":"Provide EITHER `emailType` (template mode) OR `subject` + `body` (free-form mode), not both.","properties":{"emailType":{"type":"string","description":"The program email template to trigger (template mode). Send the camelCase email-type key. The available types depend on the program type; the template's `isEnabled` setting controls automatic sends only. This endpoint can trigger any sendable template. System and transactional types (login link, PayPal confirmation, tax notifications) and the invite email cannot be sent. Referral programs: `welcomeNonReferred`, `referralLinkViewedFirstTime`, `referralLinkUsed`, `referredSignup`, `welcomeReferred`, `goalAchieved`, `campaignEndedWinners`, `campaignEndedNonWinners`, `progressUpdateMonthly`. Affiliate programs: `welcomeNonReferred`, `referralLinkViewedFirstTime`, `referredSignup`, `commissionGenerated`, `commissionAdjusted`, `payoutPending`, `payoutSentSuccess`, `progressUpdateMonthly`."},"subject":{"type":"string","maxLength":255,"description":"Subject line for a free-form email. Supports dynamic text (`{{...}}` tokens), the same as the body."},"body":{"type":"string","description":"HTML body for a free-form email. You can personalize it with dynamic text, inserting `{{...}}` tokens like `{{firstName}}` or `{{shareUrl}}`. See [Guide to using dynamic text in GrowSurf emails](https://support.growsurf.com/article/213-guide-to-using-dynamic-text-in-growsurf-emails)."},"preheader":{"type":"string","description":"Optional preheader text for a free-form email."}}},"EmailParticipantResponse":{"type":"object","required":["success","status"],"properties":{"success":{"description":"Whether the email request was accepted.","type":"boolean"},"status":{"type":"string","enum":["queued"],"description":"The email was accepted for delivery."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/email":{"post":{"tags":["Participants"],"operationId":"emailParticipant","summary":"Email participant","description":"Sends an email to a participant. Provide EITHER `emailType` to trigger one of the program's configured email templates, OR `subject` + `body` for a free-form email. Free-form emails are sent with the same compliance handling (company name, postal address, and an unsubscribe link are added automatically, and unsubscribed participants are suppressed). Sending requires the team to be verified by GrowSurf. Requires a **verified custom email domain** on the program (which can be completed in *Campaign Editor > 3. Emails > Email Settings*). Returns `400` until one is verified. The email is accepted for delivery.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailParticipantRequest"}}}},"responses":{"200":{"description":"Email accepted for delivery.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailParticipantResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Retrieve participant analytics

> Retrieves analytics for a single participant — all-time engagement counters, leaderboard ranks, and per-channel share counts (plus affiliate money metrics for affiliate programs). Useful for segmenting and re-engaging participants. Pass \`include=series\` to also get this participant's own activity over time.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Analytics","description":"Program analytics."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}},"AnalyticsDays":{"name":"days","in":"query","required":false,"description":"Last number of days to retrieve analytics for. Defaults to 365. Maximum 1825.","schema":{"type":"integer","minimum":1,"maximum":1825,"default":365}},"AnalyticsStartDate":{"name":"startDate","in":"query","required":false,"description":"Start date of the analytics timeframe as a Unix timestamp in milliseconds. Required if `days` is not set.","schema":{"type":"integer","format":"int64"}},"AnalyticsEndDate":{"name":"endDate","in":"query","required":false,"description":"End date of the analytics timeframe as a Unix timestamp in milliseconds. Required if `days` is not set.","schema":{"type":"integer","format":"int64"}}},"schemas":{"ParticipantAnalyticsResponse":{"type":"object","required":["analytics","ranks","shareCount"],"properties":{"analytics":{"description":"Participant analytics totals.","type":"object","properties":{"referrals":{"description":"All-time referrals credited to this participant.","type":"integer"},"monthlyReferrals":{"description":"Referrals credited to this participant in the current month.","type":"integer"},"leads":{"description":"Pending referral credits for this participant.","type":"integer"},"expiredReferrals":{"description":"Expired referral credits for this participant.","type":"integer"},"impressions":{"description":"Total referral-link views for this participant.","type":"integer"},"uniqueImpressions":{"description":"Unique referral-link views for this participant.","type":"integer"},"invitesSent":{"description":"Invites sent by this participant.","type":"integer"},"rewardsEarned":{"description":"Approved rewards earned by this participant.","type":"integer"},"pendingRewards":{"description":"Rewards earned by this participant that are awaiting approval.","type":"integer"},"referralRevenue":{"type":"integer","description":"Affiliate only. Revenue attributed to this participant's referrals, in minor currency units."},"totalCommissions":{"type":"integer","description":"Affiliate only. Total commissions earned, in minor currency units."},"totalPaidOut":{"type":"integer","description":"Affiliate only. Total paid out, in minor currency units."},"upcomingPayout":{"type":"integer","description":"Affiliate only. Approved commissions ready to pay, in minor currency units."},"currencyISO":{"description":"Program currency for this participant's money metrics.","type":"string"}}},"ranks":{"description":"Leaderboard ranks for this participant.","type":"object","properties":{"rank":{"type":"integer","nullable":true,"description":"All-time rank (1-indexed), or null when unranked."},"monthlyRank":{"description":"Current-month rank, or `null` when unranked.","type":"integer","nullable":true},"prevMonthlyRank":{"description":"Previous-month rank, or `null` when unranked.","type":"integer","nullable":true}}},"shareCount":{"type":"object","additionalProperties":{"type":"integer"},"description":"Per-channel share counts (e.g. `email`, `facebook`, `twitter`, ...)."},"series":{"type":"array","description":"Present only when `include=series`. This participant's own referral-link activity per period (ascending), windowed by `days`/`startDate`/`endDate` and bucketed by `interval`.","items":{"allOf":[{"type":"object","properties":{"periodStart":{"type":"integer","format":"int64","description":"Start of the period, as a Unix timestamp in milliseconds (UTC)."}}},{"$ref":"#/components/schemas/CampaignAnalytics"}]}},"startDate":{"type":"integer","format":"int64","description":"Present only with `include=series`. Window start (Unix ms)."},"endDate":{"type":"integer","format":"int64","description":"Present only with `include=series`. Window end (Unix ms)."}}},"CampaignAnalytics":{"type":"object","properties":{"invites":{"description":"Number of invites sent by participants.","type":"integer"},"impressions":{"description":"Total referral-link views.","type":"integer"},"uniqueImpressions":{"description":"Unique referral-link views.","type":"integer"},"participants":{"description":"Number of participants added.","type":"integer"},"referrals":{"description":"Number of referrals credited.","type":"integer"},"referralCreditPendings":{"description":"Number of referrals waiting for credit.","type":"integer"},"referralCreditExpireds":{"description":"Number of referrals whose credit window expired.","type":"integer"},"emailShares":{"description":"Number of shares through email.","type":"integer"},"facebookShares":{"description":"Number of shares through Facebook.","type":"integer"},"twitterShares":{"description":"Number of shares through Twitter/X.","type":"integer"},"threadsShares":{"description":"Number of shares through Threads.","type":"integer"},"blueskyShares":{"description":"Number of shares through Bluesky.","type":"integer"},"pinterestShares":{"description":"Number of shares through Pinterest.","type":"integer"},"linkedInShares":{"description":"Number of shares through LinkedIn.","type":"integer"},"smsShares":{"description":"Number of shares through SMS.","type":"integer"},"messengerShares":{"description":"Number of shares through Messenger.","type":"integer"},"whatsAppShares":{"description":"Number of shares through WhatsApp.","type":"integer"},"wechatShares":{"description":"Number of shares through WeChat.","type":"integer"},"telegramShares":{"description":"Number of shares through Telegram.","type":"integer"},"qrcodeShares":{"description":"Number of shares through QR code.","type":"integer"},"redditShares":{"description":"Number of shares through Reddit.","type":"integer"},"tumblrShares":{"description":"Number of shares through Tumblr.","type":"integer"},"copyRefLinkShares":{"description":"Number of times participants copied their referral link.","type":"integer"},"iosNativeShares":{"description":"Number of shares through iOS native sharing.","type":"integer"},"androidNativeShares":{"description":"Number of shares through Android native sharing.","type":"integer"},"totalRevenue":{"type":"integer","description":"Affiliate programs only. Revenue in the smallest unit of the program currency."},"totalCommissions":{"type":"integer","description":"Affiliate programs only. Commissions in the smallest unit of the program currency."},"totalCommissionCount":{"type":"integer","description":"Affiliate programs only. Number of commission records."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/analytics":{"get":{"tags":["Analytics"],"operationId":"retrieveParticipantAnalytics","summary":"Retrieve participant analytics","description":"Retrieves analytics for a single participant — all-time engagement counters, leaderboard ranks, and per-channel share counts (plus affiliate money metrics for affiliate programs). Useful for segmenting and re-engaging participants. Pass `include=series` to also get this participant's own activity over time.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"},{"name":"include","in":"query","description":"Set to `series` to also return this participant's own activity per period.","schema":{"type":"string","enum":["series"]}},{"$ref":"#/components/parameters/AnalyticsDays"},{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"name":"interval","in":"query","description":"Bucket size for the `series` (only used with `include=series`). Defaults to `day`.","schema":{"type":"string","enum":["day","week","month"],"default":"day"}}],"responses":{"200":{"description":"Participant analytics returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantAnalyticsResponse"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## List participant activity logs

> Returns a participant's activity logs, most recent first (offset/limit paginated).

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participants","description":"Program participant retrieval, creation, updates, deletion, and referral triggering."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"ParticipantActivityLogsResponse":{"type":"object","required":["activityLogs","limit"],"properties":{"activityLogs":{"description":"Activity log entries for the participant.","type":"array","items":{"$ref":"#/components/schemas/ParticipantActivityLog"}},"offset":{"type":"integer","nullable":true,"description":"The offset for the next page, or null when there are no more logs."},"limit":{"description":"Number of activity logs returned per page.","type":"integer"}}},"ParticipantActivityLog":{"type":"object","required":["type","text","createdAt"],"properties":{"type":{"type":"string","description":"The activity family (e.g. `REFERRAL`, `SHARE`, `REWARD`, `EMAIL`, `COMMON`)."},"text":{"type":"string","description":"A human-readable description of the activity."},"createdAt":{"type":"integer","format":"int64","description":"When the activity occurred, as a Unix timestamp in milliseconds."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/activity-logs":{"get":{"tags":["Participants"],"operationId":"listParticipantActivityLogs","summary":"List participant activity logs","description":"Returns a participant's activity logs, most recent first (offset/limit paginated).","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"},{"name":"limit","in":"query","description":"Number of logs to return (1–100, default 20).","schema":{"type":"integer","minimum":1,"maximum":100,"default":20}},{"name":"offset","in":"query","description":"Number of logs to skip.","schema":{"type":"integer","minimum":0,"default":0}}],"responses":{"200":{"description":"Activity logs returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantActivityLogsResponse"}}}},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## PARTICIPANT REWARDS ↓

## List participant rewards

> Retrieves a paged list of rewards earned by a participant.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participant Rewards","description":"Participant reward retrieval and manual reward operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}}},"schemas":{"ParticipantRewardListResponse":{"type":"object","required":["rewards","limit","nextId"],"properties":{"rewards":{"description":"Participant rewards returned for this page.","type":"array","items":{"$ref":"#/components/schemas/ParticipantReward"}},"limit":{"description":"Maximum number of rewards requested for this page.","type":"integer"},"nextId":{"description":"Reward ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]}}},"ParticipantReward":{"type":"object","description":"A reward that a participant has earned. This is different from a program `Reward` Object and contains information pertinent only to the participant that earned the reward.","required":["id","rewardId","status"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant's reward. This is different for every new reward the participant earns."},"rewardId":{"type":"string","readOnly":true,"description":"The ID of the program `Reward` (`CampaignReward`) that this participant has earned."},"status":{"$ref":"#/components/schemas/RewardStatus","readOnly":true,"description":"The status of the participant's reward."},"unread":{"type":"boolean","readOnly":true,"description":"`true` if the participant has not yet seen the reward in a GrowSurf window."},"approved":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been approved."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was approved for this participant (UTC milliseconds). `null` for unapproved rewards."},"fulfilledAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"The date and time the reward was fulfilled for this participant (UTC milliseconds). `null` for unapproved or unfulfilled rewards."},"isReferrer":{"type":"boolean","readOnly":true,"description":"`true` if the participant earned the reward as the referrer; `false` if they earned it as the referred friend (only applicable for double-sided reward types)."},"isAvailable":{"type":"boolean","readOnly":true,"description":"`true` if the reward is available for the participant to claim or redeem."},"isFulfilled":{"type":"boolean","readOnly":true,"description":"`true` if the participant's reward has been fulfilled."},"referredId":{"type":"string","readOnly":true,"description":"The ID of the friend that was referred."},"referrerId":{"type":"string","readOnly":true,"description":"The ID of the participant that made the referral."},"commissionStructure":{"readOnly":true,"oneOf":[{"$ref":"#/components/schemas/CommissionStructure"},{"type":"null"}],"description":"The commission structure associated with this reward. Present only for affiliate programs."}}},"RewardStatus":{"type":"string","enum":["PENDING","FULFILLED"]},"CommissionStructure":{"type":"object","description":"The commission configuration for an affiliate reward. Present only for affiliate programs.","properties":{"amount":{"type":["integer","null"],"description":"Fixed commission amount in the currency's smallest denomination, used when `type` is `FIXED`. `null` for percentage-based commissions."},"amountISO":{"type":["string","null"],"description":"ISO 4217 currency code for the fixed `amount`. Defaults to the program's currency when omitted. Must match the campaign `currencyISO` when provided. `null` for percentage-based commissions."},"event":{"type":["string","null"],"description":"The event that generates a commission (e.g., `SALE`)."},"type":{"type":["string","null"],"enum":["PERCENT","FIXED",null],"description":"How the commission is calculated: `PERCENT` (a percentage of the sale) or `FIXED` (a fixed `amount`)."},"minPaidReferrals":{"type":["integer","null"],"description":"The minimum number of paid referrals required before commissions are earned."},"holdDuration":{"type":["integer","null"],"description":"Number of days a commission is held before it can be paid out."},"duration":{"type":["string","null"],"description":"How long commissions continue to be earned for a referred customer."},"durationInMonths":{"type":["integer","null"],"description":"When `duration` is repeating, the number of months over which commissions are earned."},"approvalRequired":{"type":["boolean","null"],"description":"`true` if commissions require manual approval before they can be paid out."},"percent":{"type":["number","null"],"description":"The commission percentage, used when `type` is `PERCENT`."},"hasMaxAmount":{"type":["boolean","null"],"description":"`true` if a maximum commission amount cap is configured."},"maxAmount":{"type":["integer","null"],"description":"The maximum commission amount cap in the currency's smallest denomination. `null` if no cap is set."},"maxAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `maxAmount`. Must match the campaign `currencyISO` when provided."},"hasIntro":{"type":["boolean","null"],"description":"`true` if an introductory commission rate is configured."},"introType":{"type":["string","null"],"description":"How the introductory commission is calculated: `PERCENT` or `FIXED`."},"introPercent":{"type":["number","null"],"description":"The introductory commission percentage, used when `introType` is `PERCENT`."},"introAmount":{"type":["integer","null"],"description":"The introductory commission amount in the currency's smallest denomination, used when `introType` is `FIXED`."},"introAmountISO":{"type":["string","null"],"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code for `introAmount`. Must match the campaign `currencyISO` when provided."},"introDuration":{"type":["string","null"],"description":"How long the introductory rate applies."},"introDurationInMonths":{"type":["integer","null"],"description":"When `introDuration` is repeating, the number of months the introductory rate applies."}},"additionalProperties":false},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/rewards":{"get":{"tags":["Participant Rewards"],"operationId":"listParticipantRewards","summary":"List participant rewards","description":"Retrieves a paged list of rewards earned by a participant.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"},{"$ref":"#/components/parameters/NextId"},{"$ref":"#/components/parameters/Limit100"}],"responses":{"200":{"description":"Participant rewards returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantRewardListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Approve participant reward

> Approves a manually approved reward earned by a participant. This requires \`reward:write\`. When the request also sets \`fulfill\` to \`true\`, it additionally requires \`reward:fulfill\`.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participant Rewards","description":"Participant reward retrieval and manual reward operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"RewardId":{"name":"rewardId","in":"path","required":true,"description":"Participant reward ID.","schema":{"type":"string"}}},"schemas":{"ApproveRewardRequest":{"type":"object","properties":{"fulfill":{"type":"boolean","default":false,"description":"Set true to mark the reward as fulfilled after approval."}}},"SuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"description":"Whether the request succeeded.","type":"boolean"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"Success":{"description":"Success response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InvalidState":{"description":"Resource is not in a state that permits this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/reward/{rewardId}/approve":{"post":{"tags":["Participant Rewards"],"operationId":"approveParticipantReward","summary":"Approve participant reward","description":"Approves a manually approved reward earned by a participant. This requires `reward:write`. When the request also sets `fulfill` to `true`, it additionally requires `reward:fulfill`.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/RewardId"}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApproveRewardRequest"}}}},"responses":{"200":{"$ref":"#/components/responses/Success"},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"406":{"$ref":"#/components/responses/InvalidState"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Fulfill participant reward

> Marks an approved participant reward as fulfilled.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participant Rewards","description":"Participant reward retrieval and manual reward operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"RewardId":{"name":"rewardId","in":"path","required":true,"description":"Participant reward ID.","schema":{"type":"string"}}},"responses":{"Success":{"description":"Success response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InvalidState":{"description":"Resource is not in a state that permits this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}},"schemas":{"SuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"description":"Whether the request succeeded.","type":"boolean"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}}},"paths":{"/campaign/{id}/reward/{rewardId}/fulfill":{"post":{"tags":["Participant Rewards"],"operationId":"fulfillParticipantReward","summary":"Fulfill participant reward","description":"Marks an approved participant reward as fulfilled.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/RewardId"}],"responses":{"200":{"$ref":"#/components/responses/Success"},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"406":{"$ref":"#/components/responses/InvalidState"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Delete participant reward

> Removes a manually approved participant reward that has not already been approved.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Participant Rewards","description":"Participant reward retrieval and manual reward operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"RewardId":{"name":"rewardId","in":"path","required":true,"description":"Participant reward ID.","schema":{"type":"string"}}},"responses":{"Success":{"description":"Success response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InvalidState":{"description":"Resource is not in a state that permits this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}},"schemas":{"SuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"description":"Whether the request succeeded.","type":"boolean"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}}},"paths":{"/campaign/{id}/reward/{rewardId}":{"delete":{"tags":["Participant Rewards"],"operationId":"deleteParticipantReward","summary":"Delete participant reward","description":"Removes a manually approved participant reward that has not already been approved.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/RewardId"}],"responses":{"200":{"$ref":"#/components/responses/Success"},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"406":{"$ref":"#/components/responses/InvalidState"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## REFERRALS AND INVITES ↓

## List referrals and invites

> Retrieves a list of all referrals and email invites made by participants in a program.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Referrals and Invites","description":"Referral, invite, and participant email invite operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ReferralSortBy":{"name":"sortBy","in":"query","required":false,"description":"Field used to sort referral results.","schema":{"type":"string","enum":["updatedAt","createdAt","email","firstName","lastName","referralStatus","referralTriggeredAt"],"default":"updatedAt"}},"Desc":{"name":"desc","in":"query","required":false,"description":"Return results in descending order when true.","schema":{"type":"boolean","default":true}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}},"Offset":{"name":"offset","in":"query","required":false,"description":"Offset number used to skip through a result set.","schema":{"type":"integer","minimum":0,"default":0}},"ReferralEmailFilter":{"name":"email","in":"query","required":false,"description":"URL-encoded email value to filter referral results.","schema":{"type":"string"}},"FirstNameFilter":{"name":"firstName","in":"query","required":false,"description":"First name value to filter results.","schema":{"type":"string"}},"LastNameFilter":{"name":"lastName","in":"query","required":false,"description":"Last name value to filter results.","schema":{"type":"string"}},"ReferralStatusFilter":{"name":"referralStatus","in":"query","required":false,"description":"Referral status to filter results.","schema":{"$ref":"#/components/schemas/ReferralStatus"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}}},"schemas":{"ReferralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED","INVITE_SENT"]},"ReferralListResponse":{"type":"object","required":["referrals","limit","more"],"properties":{"referrals":{"description":"Referrals and invites returned for this page.","type":"array","items":{"$ref":"#/components/schemas/Referral"}},"limit":{"description":"Maximum number of referrals requested for this page.","type":"integer"},"nextOffset":{"description":"Offset to pass as `offset` for the next page, or `null` when offset pagination is not used.","type":["integer","null"]},"nextId":{"description":"Referral ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]},"more":{"description":"Whether another page of referrals is available.","type":"boolean"}}},"Referral":{"type":"object","required":["id","email","referralStatus","referredBy","createdAt","updatedAt"],"properties":{"id":{"description":"Participant ID for the referred friend or invitee.","type":"string"},"email":{"description":"Email address for the referred friend or invitee.","type":"string"},"firstName":{"description":"First name for the referred friend or invitee.","type":["string","null"]},"lastName":{"description":"Last name for the referred friend or invitee.","type":["string","null"]},"referralStatus":{"description":"Current referral status for this referred friend or invite.","$ref":"#/components/schemas/ReferralStatus"},"referredBy":{"description":"Participant ID of the referrer.","type":"string"},"createdAt":{"description":"When the referral or invite was created, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"},"updatedAt":{"description":"When the referral or invite was last updated, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/referrals":{"get":{"tags":["Referrals and Invites"],"operationId":"listReferrals","summary":"List referrals and invites","description":"Retrieves a list of all referrals and email invites made by participants in a program.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ReferralSortBy"},{"$ref":"#/components/parameters/Desc"},{"$ref":"#/components/parameters/Limit100"},{"$ref":"#/components/parameters/Offset"},{"$ref":"#/components/parameters/ReferralEmailFilter"},{"$ref":"#/components/parameters/FirstNameFilter"},{"$ref":"#/components/parameters/LastNameFilter"},{"$ref":"#/components/parameters/ReferralStatusFilter"},{"$ref":"#/components/parameters/NextId"}],"responses":{"200":{"description":"Referrals and invites returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## List participant referrals and invites

> Retrieves referrals and email invites made by a participant.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Referrals and Invites","description":"Referral, invite, and participant email invite operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}},"ReferralSortBy":{"name":"sortBy","in":"query","required":false,"description":"Field used to sort referral results.","schema":{"type":"string","enum":["updatedAt","createdAt","email","firstName","lastName","referralStatus","referralTriggeredAt"],"default":"updatedAt"}},"Desc":{"name":"desc","in":"query","required":false,"description":"Return results in descending order when true.","schema":{"type":"boolean","default":true}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}},"Offset":{"name":"offset","in":"query","required":false,"description":"Offset number used to skip through a result set.","schema":{"type":"integer","minimum":0,"default":0}},"ReferralEmailFilter":{"name":"email","in":"query","required":false,"description":"URL-encoded email value to filter referral results.","schema":{"type":"string"}},"FirstNameFilter":{"name":"firstName","in":"query","required":false,"description":"First name value to filter results.","schema":{"type":"string"}},"LastNameFilter":{"name":"lastName","in":"query","required":false,"description":"Last name value to filter results.","schema":{"type":"string"}},"ReferralStatusFilter":{"name":"referralStatus","in":"query","required":false,"description":"Referral status to filter results.","schema":{"$ref":"#/components/schemas/ReferralStatus"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}}},"schemas":{"ReferralStatus":{"type":"string","enum":["CREDIT_PENDING","CREDIT_AWARDED","CREDIT_EXPIRED","INVITE_SENT"]},"ReferralListResponse":{"type":"object","required":["referrals","limit","more"],"properties":{"referrals":{"description":"Referrals and invites returned for this page.","type":"array","items":{"$ref":"#/components/schemas/Referral"}},"limit":{"description":"Maximum number of referrals requested for this page.","type":"integer"},"nextOffset":{"description":"Offset to pass as `offset` for the next page, or `null` when offset pagination is not used.","type":["integer","null"]},"nextId":{"description":"Referral ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]},"more":{"description":"Whether another page of referrals is available.","type":"boolean"}}},"Referral":{"type":"object","required":["id","email","referralStatus","referredBy","createdAt","updatedAt"],"properties":{"id":{"description":"Participant ID for the referred friend or invitee.","type":"string"},"email":{"description":"Email address for the referred friend or invitee.","type":"string"},"firstName":{"description":"First name for the referred friend or invitee.","type":["string","null"]},"lastName":{"description":"Last name for the referred friend or invitee.","type":["string","null"]},"referralStatus":{"description":"Current referral status for this referred friend or invite.","$ref":"#/components/schemas/ReferralStatus"},"referredBy":{"description":"Participant ID of the referrer.","type":"string"},"createdAt":{"description":"When the referral or invite was created, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"},"updatedAt":{"description":"When the referral or invite was last updated, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/referrals":{"get":{"tags":["Referrals and Invites"],"operationId":"listParticipantReferrals","summary":"List participant referrals and invites","description":"Retrieves referrals and email invites made by a participant.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"},{"$ref":"#/components/parameters/ReferralSortBy"},{"$ref":"#/components/parameters/Desc"},{"$ref":"#/components/parameters/Limit100"},{"$ref":"#/components/parameters/Offset"},{"$ref":"#/components/parameters/ReferralEmailFilter"},{"$ref":"#/components/parameters/FirstNameFilter"},{"$ref":"#/components/parameters/LastNameFilter"},{"$ref":"#/components/parameters/ReferralStatusFilter"},{"$ref":"#/components/parameters/NextId"}],"responses":{"200":{"description":"Participant referrals and invites returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReferralListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Send participant invites

> Sends email invites on behalf of a participant to a list of email addresses. Sending invites via the API requires a \*\*verified custom email domain\*\* on the program; the request fails until one is verified.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Referrals and Invites","description":"Referral, invite, and participant email invite operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"SendInvitesRequest":{"type":"object","required":["emailAddresses","messageText","subjectText"],"properties":{"emailAddresses":{"description":"Email addresses to invite.","type":"array","minItems":1,"items":{"type":"string","format":"email"}},"messageText":{"description":"Message body for the invite email.","type":"string"},"subjectText":{"description":"Subject line for the invite email.","type":"string"}}},"SendInvitesResponse":{"type":"object","required":["success","messageType","invitesSent"],"properties":{"success":{"description":"Whether the invites request succeeded.","type":"boolean"},"messageType":{"description":"Result category for the invites request.","type":"string"},"invitesSent":{"description":"Number of invite emails sent.","type":"integer"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/invites":{"post":{"tags":["Referrals and Invites"],"operationId":"sendParticipantInvites","summary":"Send participant invites","description":"Sends email invites on behalf of a participant to a list of email addresses. Sending invites via the API requires a **verified custom email domain** on the program; the request fails until one is verified.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInvitesRequest"}}}},"responses":{"200":{"description":"Invite send result returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInvitesResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## ANALYTICS ↓

## Retrieve campaign analytics

> Retrieves analytics for a program. Pass \`interval\` to also get a time-series (\`series\`) alongside the totals, and \`include\` to add previous-period totals, status breakdowns, or derived rates — useful for detecting trends over time.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Analytics","description":"Program analytics."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"AnalyticsDays":{"name":"days","in":"query","required":false,"description":"Last number of days to retrieve analytics for. Defaults to 365. Maximum 1825.","schema":{"type":"integer","minimum":1,"maximum":1825,"default":365}},"AnalyticsStartDate":{"name":"startDate","in":"query","required":false,"description":"Start date of the analytics timeframe as a Unix timestamp in milliseconds. Required if `days` is not set.","schema":{"type":"integer","format":"int64"}},"AnalyticsEndDate":{"name":"endDate","in":"query","required":false,"description":"End date of the analytics timeframe as a Unix timestamp in milliseconds. Required if `days` is not set.","schema":{"type":"integer","format":"int64"}}},"schemas":{"CampaignAnalyticsResponse":{"type":"object","required":["analytics","startDate","endDate"],"properties":{"analytics":{"description":"Campaign analytics totals for the requested timeframe.","$ref":"#/components/schemas/CampaignAnalytics"},"startDate":{"description":"Start of the analytics timeframe, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"},"endDate":{"description":"End of the analytics timeframe, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"},"series":{"type":"array","description":"Present only when `interval` is `day`, `week`, or `month`. Per-period totals, ascending.","items":{"allOf":[{"type":"object","properties":{"periodStart":{"type":"integer","format":"int64","description":"Start of the period, as a Unix timestamp in milliseconds (UTC)."}}},{"$ref":"#/components/schemas/CampaignAnalytics"}]}},"previousPeriod":{"description":"Present only when `include` contains `previousPeriod`.","allOf":[{"$ref":"#/components/schemas/PreviousPeriodAnalytics"}]},"statusCounts":{"description":"Present only when `include` contains `statusCounts`.","allOf":[{"$ref":"#/components/schemas/CampaignStatusCounts"}]},"rates":{"description":"Present only when `include` contains `rates`.","allOf":[{"$ref":"#/components/schemas/CampaignAnalyticsRates"}]}}},"CampaignAnalytics":{"type":"object","properties":{"invites":{"description":"Number of invites sent by participants.","type":"integer"},"impressions":{"description":"Total referral-link views.","type":"integer"},"uniqueImpressions":{"description":"Unique referral-link views.","type":"integer"},"participants":{"description":"Number of participants added.","type":"integer"},"referrals":{"description":"Number of referrals credited.","type":"integer"},"referralCreditPendings":{"description":"Number of referrals waiting for credit.","type":"integer"},"referralCreditExpireds":{"description":"Number of referrals whose credit window expired.","type":"integer"},"emailShares":{"description":"Number of shares through email.","type":"integer"},"facebookShares":{"description":"Number of shares through Facebook.","type":"integer"},"twitterShares":{"description":"Number of shares through Twitter/X.","type":"integer"},"threadsShares":{"description":"Number of shares through Threads.","type":"integer"},"blueskyShares":{"description":"Number of shares through Bluesky.","type":"integer"},"pinterestShares":{"description":"Number of shares through Pinterest.","type":"integer"},"linkedInShares":{"description":"Number of shares through LinkedIn.","type":"integer"},"smsShares":{"description":"Number of shares through SMS.","type":"integer"},"messengerShares":{"description":"Number of shares through Messenger.","type":"integer"},"whatsAppShares":{"description":"Number of shares through WhatsApp.","type":"integer"},"wechatShares":{"description":"Number of shares through WeChat.","type":"integer"},"telegramShares":{"description":"Number of shares through Telegram.","type":"integer"},"qrcodeShares":{"description":"Number of shares through QR code.","type":"integer"},"redditShares":{"description":"Number of shares through Reddit.","type":"integer"},"tumblrShares":{"description":"Number of shares through Tumblr.","type":"integer"},"copyRefLinkShares":{"description":"Number of times participants copied their referral link.","type":"integer"},"iosNativeShares":{"description":"Number of shares through iOS native sharing.","type":"integer"},"androidNativeShares":{"description":"Number of shares through Android native sharing.","type":"integer"},"totalRevenue":{"type":"integer","description":"Affiliate programs only. Revenue in the smallest unit of the program currency."},"totalCommissions":{"type":"integer","description":"Affiliate programs only. Commissions in the smallest unit of the program currency."},"totalCommissionCount":{"type":"integer","description":"Affiliate programs only. Number of commission records."}}},"PreviousPeriodAnalytics":{"type":"object","description":"Totals for the equal-length window immediately preceding the requested one.","required":["analytics","startDate","endDate"],"properties":{"analytics":{"description":"Campaign analytics totals for the previous comparison timeframe.","$ref":"#/components/schemas/CampaignAnalytics"},"startDate":{"description":"Start of the previous comparison timeframe, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"},"endDate":{"description":"End of the previous comparison timeframe, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"}}},"CampaignStatusCounts":{"type":"object","description":"Status-count breakdowns. `rewardStatus` is present for every program; `affiliateStatus`, `commissionStatus`, and `payoutStatus` are present only for affiliate programs. Money amounts are in minor units of `currencyISO`.","properties":{"currencyISO":{"description":"Program currency for money amounts in the status-count breakdown.","type":"string"},"rewardStatus":{"description":"Reward counts by approval status.","type":"object","properties":{"pending":{"type":"integer","description":"Unapproved rewards awaiting fulfillment."},"approved":{"description":"Approved rewards.","type":"integer"}}},"affiliateStatus":{"type":"object","description":"Affiliate only. Participant counts keyed by affiliate status.","additionalProperties":{"type":"integer"}},"commissionStatus":{"type":"object","description":"Affiliate only. Commission counts and amounts by status.","properties":{"pending":{"description":"Pending commissions.","$ref":"#/components/schemas/CommissionStatusMetric"},"approved":{"description":"Approved commissions.","$ref":"#/components/schemas/CommissionStatusMetric"},"paid":{"description":"Paid commissions.","$ref":"#/components/schemas/CommissionStatusMetric"},"reversed":{"description":"Reversed commissions.","$ref":"#/components/schemas/CommissionStatusMetric"}}},"payoutStatus":{"type":"object","description":"Affiliate only. Payout counts and amounts by status.","properties":{"upcoming":{"description":"Upcoming payouts.","$ref":"#/components/schemas/PayoutStatusMetric"},"queued":{"description":"Queued payouts.","$ref":"#/components/schemas/PayoutStatusMetric"},"issued":{"description":"Issued payouts.","$ref":"#/components/schemas/PayoutStatusMetric"},"failed":{"description":"Failed payouts.","$ref":"#/components/schemas/PayoutStatusMetric"}}}}},"CommissionStatusMetric":{"type":"object","properties":{"count":{"description":"Number of commissions in this status.","type":"integer"},"totalAmount":{"type":"integer","description":"Total commission amount in minor currency units."},"totalRevenue":{"type":"integer","description":"Total attributed revenue in minor currency units."}}},"PayoutStatusMetric":{"type":"object","properties":{"count":{"description":"Number of payouts in this status.","type":"integer"},"totalAmount":{"type":"integer","description":"Total payout amount in minor currency units."}}},"CampaignAnalyticsRates":{"type":"object","description":"Derived referral rates, each a ratio in the range 0–1 (0 when its denominator is 0).","properties":{"referralConversionRate":{"type":"number","format":"float","description":"`referrals` divided by `uniqueImpressions`."},"participationRate":{"type":"number","format":"float","description":"`participants` divided by `uniqueImpressions`."},"sharesPerParticipant":{"type":"number","format":"float","description":"Total shares across all channels divided by `participants`."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/analytics":{"get":{"tags":["Analytics"],"operationId":"retrieveCampaignAnalytics","summary":"Retrieve campaign analytics","description":"Retrieves analytics for a program. Pass `interval` to also get a time-series (`series`) alongside the totals, and `include` to add previous-period totals, status breakdowns, or derived rates — useful for detecting trends over time.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/AnalyticsDays"},{"$ref":"#/components/parameters/AnalyticsStartDate"},{"$ref":"#/components/parameters/AnalyticsEndDate"},{"name":"interval","in":"query","description":"When set to `day`, `week`, or `month`, the response also includes a `series` array with per-period totals. Defaults to `total` (no series).","schema":{"type":"string","enum":["day","week","month","total"],"default":"total"}},{"name":"include","in":"query","description":"Comma-separated list of optional enrichments (opt-in to keep the default response lean): `previousPeriod` adds totals for the equal-length window immediately before the requested one; `statusCounts` adds reward (and, for affiliate programs, affiliate/commission/payout) status breakdowns; `rates` adds derived referral rates.","schema":{"type":"string"}}],"responses":{"200":{"description":"Campaign analytics returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CampaignAnalyticsResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

***

## AFFILIATE PROGRAMS ↓

## Record affiliate transaction

> \*\*Affiliate programs only.\*\* Records a sale made by a referred customer and generates affiliate commissions for their referrer when applicable. Requires at least one transaction identifier (externalId, transactionId, orderId, paymentId, invoiceId, paymentIntentId, or chargeId) so repeated requests can be de-duplicated — without one, a resent sale would create a second commission. Reuse the same identifier(s) when refunding.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Affiliate Programs","description":"Affiliate transaction, commission, and payout operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"RecordTransactionRequest":{"type":"object","required":["currency","grossAmount"],"properties":{"currency":{"description":"3-letter ISO 4217 currency code for the sale. Must match the program currency.","type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Za-z]{3}$"},"grossAmount":{"description":"Total sale amount in the currency's minor unit. Must be a positive integer.","type":"integer","minimum":1},"netAmount":{"description":"Net-of-tax sale amount in the currency's minor unit. Used as the commissionable base when supplied.","type":"integer","minimum":0},"taxAmount":{"description":"Tax collected in the currency's minor unit. Used to derive the commissionable base when `netAmount` is absent.","type":"integer","minimum":0},"amountCashNet":{"description":"Explicit post-tax cash amount in the currency's minor unit. Overrides derived net amounts.","type":"integer","minimum":0},"amountPaid":{"description":"Amount actually paid in the currency's minor unit, when reported by your payment processor.","type":"integer","minimum":0},"paidAt":{"description":"When the payment was captured or settled, as a Unix timestamp in milliseconds.","type":"integer","format":"int64"},"externalId":{"description":"Primary transaction identifier from your billing system. Used to detect duplicate submissions.","type":"string","maxLength":500},"transactionId":{"description":"Transaction identifier to store for this sale. Used to detect duplicate submissions.","type":"string","maxLength":500},"customerId":{"description":"Customer record identifier, such as a payment-provider customer ID.","type":"string","maxLength":500},"orderId":{"description":"Order identifier. Used to detect duplicate submissions when supplied.","type":"string","maxLength":500},"paymentId":{"description":"Payment identifier. Used to detect duplicate submissions when supplied.","type":"string","maxLength":500},"invoiceId":{"description":"Invoice identifier. Used to detect duplicate submissions when supplied.","type":"string","maxLength":500},"subscriptionId":{"description":"Subscription identifier for recurring payments.","type":"string","maxLength":500},"paymentIntentId":{"description":"Payment intent identifier. Used to detect duplicate submissions when supplied.","type":"string","maxLength":500},"chargeId":{"description":"Charge identifier. Used to detect duplicate submissions when supplied.","type":"string","maxLength":500},"description":{"description":"Freeform sale description shown in dashboard context and reports.","type":"string","maxLength":500},"invoiceTotal":{"description":"Invoice total including tax, in the currency's minor unit.","type":"integer","minimum":0},"invoiceTotalExcludingTax":{"description":"Invoice total excluding tax, in the currency's minor unit.","type":"integer","minimum":0},"invoiceSubtotalExcludingTax":{"description":"Invoice subtotal excluding tax, in the currency's minor unit.","type":"integer","minimum":0},"totalTaxAmount":{"description":"Aggregate tax amount in the currency's minor unit.","type":"integer","minimum":0},"totalTaxAmounts":{"description":"Detailed tax breakdown objects from your payment processor.","type":"array","items":{"type":"object","additionalProperties":true}},"totalTaxes":{"description":"Alternate detailed tax breakdown objects from your payment processor.","type":"array","items":{"type":"object","additionalProperties":true}}}},"RecordTransactionResponse":{"oneOf":[{"type":"object","required":["success","firstSale","duplicate","message"],"properties":{"success":{"description":"Whether the sale was recorded.","type":"boolean","const":true},"firstSale":{"description":"Whether this was the referred customer's first recorded sale.","type":"boolean"},"duplicate":{"description":"Always `false` when the sale is newly recorded.","type":"boolean","const":false},"message":{"description":"Human-readable result message for the sale.","type":"string"}}},{"type":"object","required":["success","duplicate","commissionsCreated","duplicateFields","matchingCommissionIds","message"],"properties":{"success":{"description":"Always `false` when the sale matched an existing transaction.","type":"boolean","const":false},"duplicate":{"description":"Always `true` when the sale matched an existing transaction.","type":"boolean","const":true},"commissionsCreated":{"description":"Number of commissions created by this duplicate request.","type":"integer"},"duplicateFields":{"description":"Identifier fields that matched an existing transaction.","type":"array","items":{"type":"string"}},"matchingCommissionIds":{"description":"Commission IDs that matched the submitted identifiers.","type":"array","items":{"type":"string"}},"message":{"description":"Human-readable result message for the duplicate sale.","type":"string"}}}]},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/transaction":{"post":{"tags":["Affiliate Programs"],"operationId":"recordTransaction","summary":"Record affiliate transaction","description":"**Affiliate programs only.** Records a sale made by a referred customer and generates affiliate commissions for their referrer when applicable. Requires at least one transaction identifier (externalId, transactionId, orderId, paymentId, invoiceId, paymentIntentId, or chargeId) so repeated requests can be de-duplicated — without one, a resent sale would create a second commission. Reuse the same identifier(s) when refunding.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordTransactionRequest"}}}},"responses":{"200":{"description":"Transaction result returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordTransactionResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Refund or amend affiliate transaction

> \*\*Affiliate programs only.\*\* Records an amendment (refund, partial refund, refund cancellation, or chargeback) against a previously recorded transaction and reverses or adjusts the referrer's commission. The inverse of Record Affiliate Transaction. Identify the original transaction with the same identifier(s) you sent when recording it. Commissions already paid out to the affiliate are not clawed back; the amendment is recorded for tax reporting only.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Affiliate Programs","description":"Affiliate transaction, commission, and payout operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}}},"schemas":{"RefundTransactionRequest":{"type":"object","description":"At least one transaction identifier (externalId, transactionId, orderId, paymentId, invoiceId, paymentIntentId, or chargeId) is required to locate the original transaction. Send the same one(s) you used when recording the transaction.","properties":{"amendmentType":{"type":"string","enum":["REFUND","CHARGEBACK"],"default":"REFUND","description":"REFUND covers full refunds, partial refunds, and refund cancellations; CHARGEBACK is always a full reversal."},"amountRefunded":{"type":"integer","minimum":0,"description":"Cumulative amount refunded so far, in the currency's minor unit. Omit for a full refund. For a partial refund send the running total, not the per-refund delta."},"amount":{"type":"integer","minimum":1,"description":"Original sale gross (minor units). Optional — the value stored when the transaction was recorded is used when available; only needed for partial refunds of older records."},"refundId":{"type":"string","maxLength":500,"description":"Stable per-refund identifier. Recommended for partial refunds so repeated calls stay idempotent."},"refundStatus":{"type":"string","maxLength":500,"description":"Refund status. Send \"canceled\" with a lowered amountRefunded to restore a previously reduced commission."},"refundAmount":{"type":"integer","minimum":0,"description":"The per-refund delta (minor units). Optional bookkeeping field."},"currency":{"type":"string","minLength":3,"maxLength":3,"pattern":"^[A-Za-z]{3}$","description":"3-letter ISO currency. Optional — resolved from the original commission when available."},"externalId":{"description":"Original transaction identifier from your billing system.","type":"string","maxLength":500},"transactionId":{"description":"Original transaction identifier to match against.","type":"string","maxLength":500},"orderId":{"description":"Original order identifier to match against.","type":"string","maxLength":500},"paymentId":{"description":"Original payment identifier to match against.","type":"string","maxLength":500},"invoiceId":{"description":"Original invoice identifier to match against.","type":"string","maxLength":500},"paymentIntentId":{"description":"Original payment intent identifier to match against.","type":"string","maxLength":500},"chargeId":{"description":"Original charge identifier to match against.","type":"string","maxLength":500},"description":{"description":"Freeform amendment description shown in dashboard context and reports.","type":"string","maxLength":500}}},"RefundTransactionResponse":{"type":"object","required":["success","amendmentType","matched","reversed","adjusted","deleted","matchingCommissionIds","message"],"properties":{"success":{"type":"boolean","description":"true when the amendment was processed (including the tax-only case for already-paid commissions); false when no matching transaction was found."},"notFound":{"type":"boolean","description":"Present and true when no commission matched the provided identifiers."},"amendmentType":{"description":"Amendment type that was processed.","type":"string","enum":["REFUND","CHARGEBACK"]},"matched":{"type":"integer","description":"Number of commissions found for the provided identifiers."},"reversed":{"type":"integer","description":"Number of commissions reversed (set to zero amount)."},"adjusted":{"type":"integer","description":"Number of commissions partially adjusted."},"deleted":{"description":"Number of pending commissions deleted by the amendment.","type":"integer"},"matchingCommissionIds":{"description":"Commission IDs that matched the submitted identifiers.","type":"array","items":{"type":"string"}},"message":{"description":"Human-readable result message for the amendment.","type":"string"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/transaction/refund":{"post":{"tags":["Affiliate Programs"],"operationId":"refundTransaction","summary":"Refund or amend affiliate transaction","description":"**Affiliate programs only.** Records an amendment (refund, partial refund, refund cancellation, or chargeback) against a previously recorded transaction and reverses or adjusts the referrer's commission. The inverse of Record Affiliate Transaction. Identify the original transaction with the same identifier(s) you sent when recording it. Commissions already paid out to the affiliate are not clawed back; the amendment is recorded for tax reporting only.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundTransactionRequest"}}}},"responses":{"200":{"description":"Amendment result returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RefundTransactionResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## List participant commissions

> \*\*Affiliate programs only.\*\* Retrieves a paged list of all participant commissions in an affiliate program.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Affiliate Programs","description":"Affiliate transaction, commission, and payout operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}},"CommissionStatus":{"name":"status","in":"query","required":false,"description":"Participant commission status.","schema":{"$ref":"#/components/schemas/CommissionStatus"}}},"schemas":{"CommissionStatus":{"type":"string","enum":["PENDING","APPROVED","PAID","REVERSED","DELETED"]},"ParticipantCommissionListResponse":{"type":"object","required":["commissions","limit","nextId"],"properties":{"commissions":{"description":"Participant commissions returned for this page.","type":"array","items":{"$ref":"#/components/schemas/ParticipantCommission"}},"limit":{"description":"Maximum number of commissions requested for this page.","type":"integer"},"nextId":{"description":"Commission ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]}}},"ParticipantCommission":{"type":"object","description":"**Affiliate programs only.** A commission generated for a (referrer) participant in an affiliate program.","required":["id","referrerId","referredId","amount","saleAmount","currencyISO","status","createdAt"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant commission."},"referrerId":{"type":"string","readOnly":true,"description":"The participant ID of the referrer who earned the commission."},"referredId":{"type":"string","readOnly":true,"description":"The participant ID of the referred friend (the conversion that triggered the commission)."},"amount":{"type":["integer","null"],"readOnly":true,"description":"Commission amount (what the affiliate earned) in the currency's smallest denomination (e.g., `100` cents equals $1.00 USD, and `100` equals ¥100 for a zero-decimal currency). Always non-negative when present; `null` if it could not be computed."},"saleAmount":{"type":["integer","null"],"readOnly":true,"description":"The sale amount from the transaction the referral paid, in the currency's smallest denomination. Always non-negative when present; `null` for commissions whose provider does not report a sale amount."},"currencyISO":{"type":"string","readOnly":true,"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code of the commission (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/CommissionStatus","readOnly":true,"description":"Current lifecycle state of the commission."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission was approved. `null` until the commission transitions to `APPROVED`."},"paidAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission was sent in a payout. `null` until the commission transitions to `PAID`."},"reversedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission was reversed (for example, due to a refund or chargeback). `null` unless the commission transitions to `REVERSED`."},"holdDuration":{"type":["integer","null"],"readOnly":true,"description":"Number of days the commission must age before it can be processed for payout. Defaults to `0` (no hold)."},"payoutQueuedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission was queued for payout. `null` if it has not been scheduled."},"provider":{"type":["string","null"],"readOnly":true,"description":"Origin of the commission event. For example, `stripe` or `api`."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission record was created."},"amountInCampaignCurrency":{"type":["integer","null"],"readOnly":true,"description":"Commission amount converted to the program's currency, in the currency's smallest denomination. Always non-negative. `null` if the commission is still pending FX conversion."},"saleAmountInCampaignCurrency":{"type":["integer","null"],"readOnly":true,"description":"The sale amount converted to the program's currency, in the currency's smallest denomination. Always non-negative. `null` if the commission is still pending FX conversion."},"exchangeRate":{"type":["number","null"],"readOnly":true,"description":"The currency exchange rate used for conversion. `null` if the commission is still pending FX conversion."},"campaignCurrencyISO":{"type":["string","null"],"readOnly":true,"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code of the program used in currency conversion (e.g., `USD`, `GBP`). `null` if the commission is still pending FX conversion."},"exchangeRateAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the currency exchange rate was received. `null` if the commission is still pending FX conversion."},"fxError":{"type":["string","null"],"readOnly":true,"description":"If there was an error with FX conversion, the specific details. `null` if conversion was successful."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/commissions":{"get":{"tags":["Affiliate Programs"],"operationId":"listCommissions","summary":"List participant commissions","description":"**Affiliate programs only.** Retrieves a paged list of all participant commissions in an affiliate program.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/NextId"},{"$ref":"#/components/parameters/Limit100"},{"$ref":"#/components/parameters/CommissionStatus"}],"responses":{"200":{"description":"Participant commissions returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantCommissionListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## List commissions for a participant

> \*\*Affiliate programs only.\*\* Retrieves a paged list of commissions earned by a participant.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Affiliate Programs","description":"Affiliate transaction, commission, and payout operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}},"CommissionStatus":{"name":"status","in":"query","required":false,"description":"Participant commission status.","schema":{"$ref":"#/components/schemas/CommissionStatus"}}},"schemas":{"CommissionStatus":{"type":"string","enum":["PENDING","APPROVED","PAID","REVERSED","DELETED"]},"ParticipantCommissionListResponse":{"type":"object","required":["commissions","limit","nextId"],"properties":{"commissions":{"description":"Participant commissions returned for this page.","type":"array","items":{"$ref":"#/components/schemas/ParticipantCommission"}},"limit":{"description":"Maximum number of commissions requested for this page.","type":"integer"},"nextId":{"description":"Commission ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]}}},"ParticipantCommission":{"type":"object","description":"**Affiliate programs only.** A commission generated for a (referrer) participant in an affiliate program.","required":["id","referrerId","referredId","amount","saleAmount","currencyISO","status","createdAt"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant commission."},"referrerId":{"type":"string","readOnly":true,"description":"The participant ID of the referrer who earned the commission."},"referredId":{"type":"string","readOnly":true,"description":"The participant ID of the referred friend (the conversion that triggered the commission)."},"amount":{"type":["integer","null"],"readOnly":true,"description":"Commission amount (what the affiliate earned) in the currency's smallest denomination (e.g., `100` cents equals $1.00 USD, and `100` equals ¥100 for a zero-decimal currency). Always non-negative when present; `null` if it could not be computed."},"saleAmount":{"type":["integer","null"],"readOnly":true,"description":"The sale amount from the transaction the referral paid, in the currency's smallest denomination. Always non-negative when present; `null` for commissions whose provider does not report a sale amount."},"currencyISO":{"type":"string","readOnly":true,"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code of the commission (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/CommissionStatus","readOnly":true,"description":"Current lifecycle state of the commission."},"approvedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission was approved. `null` until the commission transitions to `APPROVED`."},"paidAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission was sent in a payout. `null` until the commission transitions to `PAID`."},"reversedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission was reversed (for example, due to a refund or chargeback). `null` unless the commission transitions to `REVERSED`."},"holdDuration":{"type":["integer","null"],"readOnly":true,"description":"Number of days the commission must age before it can be processed for payout. Defaults to `0` (no hold)."},"payoutQueuedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission was queued for payout. `null` if it has not been scheduled."},"provider":{"type":["string","null"],"readOnly":true,"description":"Origin of the commission event. For example, `stripe` or `api`."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the commission record was created."},"amountInCampaignCurrency":{"type":["integer","null"],"readOnly":true,"description":"Commission amount converted to the program's currency, in the currency's smallest denomination. Always non-negative. `null` if the commission is still pending FX conversion."},"saleAmountInCampaignCurrency":{"type":["integer","null"],"readOnly":true,"description":"The sale amount converted to the program's currency, in the currency's smallest denomination. Always non-negative. `null` if the commission is still pending FX conversion."},"exchangeRate":{"type":["number","null"],"readOnly":true,"description":"The currency exchange rate used for conversion. `null` if the commission is still pending FX conversion."},"campaignCurrencyISO":{"type":["string","null"],"readOnly":true,"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code of the program used in currency conversion (e.g., `USD`, `GBP`). `null` if the commission is still pending FX conversion."},"exchangeRateAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the currency exchange rate was received. `null` if the commission is still pending FX conversion."},"fxError":{"type":["string","null"],"readOnly":true,"description":"If there was an error with FX conversion, the specific details. `null` if conversion was successful."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/commissions":{"get":{"tags":["Affiliate Programs"],"operationId":"listParticipantCommissions","summary":"List commissions for a participant","description":"**Affiliate programs only.** Retrieves a paged list of commissions earned by a participant.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"},{"$ref":"#/components/parameters/NextId"},{"$ref":"#/components/parameters/Limit100"},{"$ref":"#/components/parameters/CommissionStatus"}],"responses":{"200":{"description":"Participant commissions returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantCommissionListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Approve participant commission

> \*\*Affiliate programs only.\*\* Approves a pending participant commission so it can become eligible for payout.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Affiliate Programs","description":"Affiliate transaction, commission, and payout operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"CommissionId":{"name":"commissionId","in":"path","required":true,"description":"Participant commission ID.","schema":{"type":"string"}}},"responses":{"Success":{"description":"Success response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InvalidState":{"description":"Resource is not in a state that permits this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}},"schemas":{"SuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"description":"Whether the request succeeded.","type":"boolean"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}}},"paths":{"/campaign/{id}/commission/{commissionId}/approve":{"post":{"tags":["Affiliate Programs"],"operationId":"approveCommission","summary":"Approve participant commission","description":"**Affiliate programs only.** Approves a pending participant commission so it can become eligible for payout.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/CommissionId"}],"responses":{"200":{"$ref":"#/components/responses/Success"},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"406":{"$ref":"#/components/responses/InvalidState"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## Delete participant commission

> \*\*Affiliate programs only.\*\* Removes a pending participant commission.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Affiliate Programs","description":"Affiliate transaction, commission, and payout operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"CommissionId":{"name":"commissionId","in":"path","required":true,"description":"Participant commission ID.","schema":{"type":"string"}}},"responses":{"Success":{"description":"Success response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"}}}},"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"InvalidState":{"description":"Resource is not in a state that permits this operation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}},"schemas":{"SuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"description":"Whether the request succeeded.","type":"boolean"}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}}},"paths":{"/campaign/{id}/commission/{commissionId}":{"delete":{"tags":["Affiliate Programs"],"operationId":"deleteCommission","summary":"Delete participant commission","description":"**Affiliate programs only.** Removes a pending participant commission.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/CommissionId"}],"responses":{"200":{"$ref":"#/components/responses/Success"},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"406":{"$ref":"#/components/responses/InvalidState"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## List participant payouts

> \*\*Affiliate programs only.\*\* Retrieves a paged list of all participant payouts in an affiliate program.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Affiliate Programs","description":"Affiliate transaction, commission, and payout operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}},"PayoutStatus":{"name":"status","in":"query","required":false,"description":"Participant payout status.","schema":{"$ref":"#/components/schemas/PayoutStatus"}}},"schemas":{"PayoutStatus":{"type":"string","enum":["UPCOMING","QUEUED","ISSUED","FAILED"]},"ParticipantPayoutListResponse":{"type":"object","required":["payouts","limit","nextId"],"properties":{"payouts":{"description":"Participant payouts returned for this page.","type":"array","items":{"$ref":"#/components/schemas/ParticipantPayout"}},"limit":{"description":"Maximum number of payouts requested for this page.","type":"integer"},"nextId":{"description":"Payout ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]}}},"ParticipantPayout":{"type":"object","description":"**Affiliate programs only.** A payout generated for a participant in an affiliate program. All payouts start with a status of `UPCOMING`, which represents an aggregation of the participant's `ParticipantCommission` amounts, and is adjusted in real time as commissions change.","required":["id","participantId","commissionIds","amount","currencyISO","status","createdAt"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant payout."},"participantId":{"type":"string","readOnly":true,"description":"The participant ID of the referrer who earned the payout."},"commissionIds":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"The IDs of the associated `ParticipantCommission`s."},"amount":{"type":"integer","readOnly":true,"description":"Payout amount in the currency's smallest denomination (e.g., `100` cents equals $1.00 USD, and `100` equals ¥100 for a zero-decimal currency). Always non-negative."},"currencyISO":{"type":"string","readOnly":true,"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code of the payout (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/PayoutStatus","readOnly":true,"description":"Current lifecycle state of the payout."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the payout record was created."},"queuedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the payout was queued for processing (status `QUEUED`). `null` until the payout is queued."},"issuedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the payout was issued. `null` if the payout was not successfully issued."},"failedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the payout failed to be issued. `null` if the payout did not fail to be issued."},"provider":{"type":["string","null"],"readOnly":true,"description":"The payment provider used to issue the payout (e.g., `paypal`). `null` for payouts with a status of `UPCOMING`."},"amountInCampaignCurrency":{"type":["integer","null"],"readOnly":true,"description":"Payout amount converted to the program's currency, in the currency's smallest denomination. Always non-negative. `null` if the payout status is `UPCOMING` or if it is still pending FX conversion."},"campaignCurrencyISO":{"type":["string","null"],"readOnly":true,"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code of the program used in currency conversion (e.g., `USD`, `GBP`). `null` if the payout status is `UPCOMING` or if it is still pending FX conversion."},"exchangeRateAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the currency exchange rate was received. `null` if the payout status is `UPCOMING` or if it is still pending FX conversion."},"exchangeRate":{"type":["number","null"],"readOnly":true,"description":"The currency exchange rate used for conversion. `null` if the payout status is `UPCOMING` or if it is still pending FX conversion."},"fxError":{"type":["string","null"],"readOnly":true,"description":"If there was an error with FX conversion, the specific details. `null` if the payout status is `UPCOMING` or if the FX conversion was successful."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/payouts":{"get":{"tags":["Affiliate Programs"],"operationId":"listPayouts","summary":"List participant payouts","description":"**Affiliate programs only.** Retrieves a paged list of all participant payouts in an affiliate program.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/NextId"},{"$ref":"#/components/parameters/Limit100"},{"$ref":"#/components/parameters/PayoutStatus"}],"responses":{"200":{"description":"Participant payouts returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantPayoutListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```

## List payouts for a participant

> \*\*Affiliate programs only.\*\* Retrieves a paged list of payouts that belong to a participant.

```json
{"openapi":"3.1.0","info":{"title":"GrowSurf REST API","version":"2.0.0"},"tags":[{"name":"Affiliate Programs","description":"Affiliate transaction, commission, and payout operations."}],"servers":[{"url":"https://api.growsurf.com/v2","description":"Production"}],"security":[{"apiKeyAuth":[]}],"components":{"securitySchemes":{"apiKeyAuth":{"type":"http","scheme":"bearer","bearerFormat":"API key","description":"GrowSurf REST API key supplied as `Authorization: Bearer <api_key>`."}},"parameters":{"CampaignId":{"name":"id","in":"path","required":true,"description":"GrowSurf program ID.","schema":{"type":"string"}},"ParticipantIdOrEmail":{"name":"participantIdOrEmail","in":"path","required":true,"description":"GrowSurf participant ID or URL-encoded participant email address.","schema":{"type":"string"}},"NextId":{"name":"nextId","in":"query","required":false,"description":"ID to start the next paged result set with.","schema":{"type":"string"}},"Limit100":{"name":"limit","in":"query","required":false,"description":"Number of results to return. Maximum 100.","schema":{"type":"integer","minimum":1,"maximum":100,"default":10}},"PayoutStatus":{"name":"status","in":"query","required":false,"description":"Participant payout status.","schema":{"$ref":"#/components/schemas/PayoutStatus"}}},"schemas":{"PayoutStatus":{"type":"string","enum":["UPCOMING","QUEUED","ISSUED","FAILED"]},"ParticipantPayoutListResponse":{"type":"object","required":["payouts","limit","nextId"],"properties":{"payouts":{"description":"Participant payouts returned for this page.","type":"array","items":{"$ref":"#/components/schemas/ParticipantPayout"}},"limit":{"description":"Maximum number of payouts requested for this page.","type":"integer"},"nextId":{"description":"Payout ID to pass as `nextId` for the next page, or `null` when there are no more results.","type":["string","null"]}}},"ParticipantPayout":{"type":"object","description":"**Affiliate programs only.** A payout generated for a participant in an affiliate program. All payouts start with a status of `UPCOMING`, which represents an aggregation of the participant's `ParticipantCommission` amounts, and is adjusted in real time as commissions change.","required":["id","participantId","commissionIds","amount","currencyISO","status","createdAt"],"properties":{"id":{"type":"string","readOnly":true,"description":"The unique identifier of the participant payout."},"participantId":{"type":"string","readOnly":true,"description":"The participant ID of the referrer who earned the payout."},"commissionIds":{"type":"array","readOnly":true,"items":{"type":"string"},"description":"The IDs of the associated `ParticipantCommission`s."},"amount":{"type":"integer","readOnly":true,"description":"Payout amount in the currency's smallest denomination (e.g., `100` cents equals $1.00 USD, and `100` equals ¥100 for a zero-decimal currency). Always non-negative."},"currencyISO":{"type":"string","readOnly":true,"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code of the payout (e.g., `USD`, `GBP`)."},"status":{"$ref":"#/components/schemas/PayoutStatus","readOnly":true,"description":"Current lifecycle state of the payout."},"createdAt":{"type":"integer","format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the payout record was created."},"queuedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the payout was queued for processing (status `QUEUED`). `null` until the payout is queued."},"issuedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the payout was issued. `null` if the payout was not successfully issued."},"failedAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the payout failed to be issued. `null` if the payout did not fail to be issued."},"provider":{"type":["string","null"],"readOnly":true,"description":"The payment provider used to issue the payout (e.g., `paypal`). `null` for payouts with a status of `UPCOMING`."},"amountInCampaignCurrency":{"type":["integer","null"],"readOnly":true,"description":"Payout amount converted to the program's currency, in the currency's smallest denomination. Always non-negative. `null` if the payout status is `UPCOMING` or if it is still pending FX conversion."},"campaignCurrencyISO":{"type":["string","null"],"readOnly":true,"description":"The [ISO 4217](https://www.iso.org/iso-4217-currency-codes.html) currency code of the program used in currency conversion (e.g., `USD`, `GBP`). `null` if the payout status is `UPCOMING` or if it is still pending FX conversion."},"exchangeRateAt":{"type":["integer","null"],"format":"int64","readOnly":true,"description":"UTC timestamp (in milliseconds) when the currency exchange rate was received. `null` if the payout status is `UPCOMING` or if it is still pending FX conversion."},"exchangeRate":{"type":["number","null"],"readOnly":true,"description":"The currency exchange rate used for conversion. `null` if the payout status is `UPCOMING` or if it is still pending FX conversion."},"fxError":{"type":["string","null"],"readOnly":true,"description":"If there was an error with FX conversion, the specific details. `null` if the payout status is `UPCOMING` or if the FX conversion was successful."}}},"Error":{"type":"object","properties":{"name":{"description":"Error name returned for the failed request.","type":"string"},"code":{"description":"Stable machine-readable error code.","type":"string"},"message":{"description":"Human-readable error message.","type":"string"},"errors":{"description":"Optional validation details for specific request fields.","type":"array","items":{"type":"object","additionalProperties":true}},"status":{"description":"HTTP status code for the error.","type":"integer"},"supportUrl":{"description":"Support URL related to the error, when available.","type":"string","format":"uri"},"policyName":{"description":"Rate-limit policy name for `429` errors, when available.","type":"string"},"level":{"description":"Error severity level, when available.","type":"string"},"timestamp":{"description":"When the error response was created.","type":"string","format":"date-time"}}}},"responses":{"BadRequest":{"description":"Invalid request.","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"}}}},"NotFound":{"description":"Resource not found.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"UnprocessableEntity":{"description":"Request is not valid for the current program or participant state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"RateLimited":{"description":"Too many requests.","headers":{"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"}}}}}},"paths":{"/campaign/{id}/participant/{participantIdOrEmail}/payouts":{"get":{"tags":["Affiliate Programs"],"operationId":"listParticipantPayouts","summary":"List payouts for a participant","description":"**Affiliate programs only.** Retrieves a paged list of payouts that belong to a participant.","parameters":[{"$ref":"#/components/parameters/CampaignId"},{"$ref":"#/components/parameters/ParticipantIdOrEmail"},{"$ref":"#/components/parameters/NextId"},{"$ref":"#/components/parameters/Limit100"},{"$ref":"#/components/parameters/PayoutStatus"}],"responses":{"200":{"description":"Participant payouts returned.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ParticipantPayoutListResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"$ref":"#/components/responses/UnprocessableEntity"},"429":{"$ref":"#/components/responses/RateLimited"}}}}}}
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.growsurf.com/developer-tools/rest-api/api-reference.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
