# auth.md You are an agent that wants to call GrowSurf on a user's behalf. This file describes the authentication and account-onboarding options that GrowSurf supports today, how to select the safest option, and how to handle credentials. Relevant hosts: - **REST API resource server** — `https://api.growsurf.com/v2` - **Hosted MCP server and OAuth authorization server** — `https://mcp.growsurf.com` - **Dashboard** — `https://app.growsurf.com` - **Documentation** — `https://docs.growsurf.com` This file follows the [auth.md](https://github.com/workos/auth.md) walkthrough order: discover, pick a method, register, claim, exchange, use the credential, handle errors, and revoke. ## Step 1 — Discover Start at the resource you want to call and read its published metadata. GrowSurf publishes two discovery documents on the hosted MCP origin: - OAuth Protected Resource Metadata (RFC 9728): `https://mcp.growsurf.com/.well-known/oauth-protected-resource` - OAuth Authorization Server Metadata (RFC 8414): `https://mcp.growsurf.com/.well-known/oauth-authorization-server` An unauthenticated MCP request returns `401` with a `WWW-Authenticate: Bearer` challenge whose `resource_metadata` parameter points at the Protected Resource Metadata document. Follow that URL rather than guessing endpoints. The REST API is discovered through its published contract instead of an OAuth challenge: - OpenAPI 3.1 document: `https://growsurf.com/openapi.json` - Arazzo 1.1 onboarding workflow: `https://growsurf.com/arazzo.yaml` - Client-neutral onboarding brief: `https://growsurf.com/agent-start.md` **Not implemented by GrowSurf.** The Authorization Server Metadata document does not carry an `agent_auth` block, so there is no `identity_endpoint`, no `claim_endpoint`, and no `identity_assertion` exchange. GrowSurf does not accept an `id-jag` identity assertion, and it does not support the `service_auth` or `anonymous` registration types. Do not call or invent those endpoints. Use the OAuth, onboarding, or API-key paths below. ## Step 2 — Pick a method GrowSurf supports four production paths: 1. **Direct REST onboarding** for users who do not have a GrowSurf account. With the owner's approval, an agent can call `POST https://api.growsurf.com/v2/accounts` without a credential. The response returns the new API key once. 2. **Direct REST or an official SDK** for users who already have a GrowSurf account and API key. 3. **Hosted MCP with OAuth** as an optional connection for users who have a GrowSurf account. The user signs in to GrowSurf, selects a team, and approves access. The agent does not handle an API key. 4. **Keyless MCP onboarding** as an optional path for users who do not have a GrowSurf account. The agent connects to `https://mcp.growsurf.com/onboard` without credentials and calls `growsurf_create_account`. Pick the path from what the user already has, not from what is most capable. Before requesting or creating a credential, check whether GrowSurf is already configured in the user's environment: 1. **Existing hosted MCP connection** — use the configured GrowSurf tools. Do not ask for an API key. 2. **Existing local GrowSurf MCP server or CLI** — use the configured `@growsurfteam/growsurf-mcp` package. Do not replace a working configuration. 3. **Existing `GROWSURF_API_KEY` secret** — use it only for direct REST or local MCP access and only within its granted scopes and program access. If one of these works, use it and stop. Do not ask the user for a credential that you do not need. ## Step 3 — Register ### No GrowSurf account: direct REST onboarding Use the client-neutral workflow at `https://growsurf.com/agent-start.md`. Before the request, confirm that the owner approves account creation and accepts the GrowSurf Terms of Service and Privacy Policy. ```http POST /v2/accounts HTTP/1.1 Host: api.growsurf.com Content-Type: application/json Accept: application/json { "email": "owner@example.com", "firstName": "Ada", "lastName": "Lovelace", "company": "Example Company" } ``` The endpoint requires a business email address. The name and company fields are optional. The response returns the API key once. Store it in the owner's secret store or `GROWSURF_API_KEY`. Do not print it, echo it, paste it into chat, or write it to a committed file. Confirm you have that secret store before you call this endpoint. A lost key cannot be recovered through the API, and a workspace that forgets files or memory between turns will lose it. Without one, use pathway 3 instead: have the owner connect `https://mcp.growsurf.com` and sign in, so the credential stays with the client. The new account includes a 14-day Business trial with no credit card. The key stays locked until the owner verifies the email address, then the same key starts working: keep it and retry rather than requesting a replacement. Unverified accounts are deleted after 7 days. After verification, an agent can create and configure a program. Keep it in `DRAFT` until the owner separately approves launch, reward fulfillment, and any payment setting. For a machine-readable workflow, use `https://growsurf.com/arazzo.yaml`. For request and response schemas, use `https://growsurf.com/openapi.json`. ### Optional path: hosted MCP with OAuth Connect a compatible remote MCP client to: ```text https://mcp.growsurf.com ``` The MCP server returns OAuth discovery information through: - Protected Resource Metadata: `https://mcp.growsurf.com/.well-known/oauth-protected-resource` - Authorization Server Metadata: `https://mcp.growsurf.com/.well-known/oauth-authorization-server` When an unauthenticated request returns `401`, follow the `resource_metadata` URL in the `WWW-Authenticate` header. Let the MCP client complete the advertised authorization-code flow. Do not ask the user to paste an OAuth access token or refresh token into the conversation. The OAuth connection acts only on the team the user selects and within the scopes the user approves. The hosted MCP server requests only scopes needed by its published tools. API-key rotation is not available through MCP. ### Optional path: keyless MCP onboarding If the user does not have an account: 1. Confirm that the user wants you to create a GrowSurf account. Account creation accepts the GrowSurf Terms of Service and Privacy Policy on the account holder's behalf. 2. Connect to `https://mcp.growsurf.com/onboard` without credentials. 3. Call `growsurf_create_account` with the user-approved account details. 4. Tell the user to complete the verification link sent to their email address. Do not ask them to paste the link, code, or email contents into the conversation. 5. Store the returned API key in the user's secret store or `GROWSURF_API_KEY` environment variable. The key is shown once. Do not repeat it in chat, logs, command output, screenshots, or files that may be committed. The new key remains locked until the team owner's email is verified. Protected endpoints return `403` with `EMAIL_NOT_VERIFIED_ERROR` until verification is complete. Accounts whose email is not verified are deleted after 7 days. Verification does not replace the key. Keep the key you were given and retry once the owner verifies. If it later returns `403` with `NOT_AUTHORIZED_ERROR`, it was revoked or rotated: discard it and ask the user to refresh the secret source with the current key. Do not ask them to paste the replacement into chat. ### Existing GrowSurf account: direct REST API or local MCP The user creates and manages additional API keys at: ```text https://app.growsurf.com/settings#api-keys ``` Ask the user to create a separate key for the integration, environment, or agent. They should grant only the required scopes and restrict the key to the required programs when possible. Look for the key in this order and stop at the first authorized source that exists: 1. `GROWSURF_API_KEY` in the process environment. 2. A project `.env` file that the user has told you to read. 3. The local MCP server's configured environment, when you are calling through that server. If no key is available and the task requires one, tell the user to create a scoped key in GrowSurf Settings and place it in their environment, secret store, or MCP client configuration. Never ask the user to paste the key into the conversation. For local stdio MCP, the official package is: ```text npx -y @growsurfteam/growsurf-mcp ``` `GROWSURF_CAMPAIGN_ID` may set a default program for program-scoped tools. It is not a credential. An explicit `campaignId` tool argument overrides it. ## Step 4 — Claim A registered credential still has to be tied to a real person before GrowSurf will act on it. The claim step is a human action in the GrowSurf product, not an agent-to-service call, and GrowSurf does not expose the `claim_endpoint`, `user_code`, or `verification_uri` ceremony described in the auth.md reference implementation. Never poll for a claim, and never ask the user to paste a verification link, code, or email contents into the conversation. | Path | What the user does | How you know it finished | | --- | --- | --- | | Direct REST or keyless MCP onboarding | The team owner opens the verification link GrowSurf emailed them. | Protected endpoints stop returning `403` with `EMAIL_NOT_VERIFIED_ERROR`. Retry with the same key. | | Hosted MCP with OAuth | The user signs in to GrowSurf, selects a team, and approves the requested scopes. | The MCP client completes authorization and the connection reports authorized tools. | | Existing account, direct REST | The user creates a scoped key in GrowSurf Settings and places it in the approved secret source. | Reading the secret source returns a key that answers `200`. | An unverified account is deleted after 7 days. Verification does not replace the key: keep the key you were issued and retry. Separately, the key issued by `POST /v2/accounts` is replaced once, the first time the owner signs in to the dashboard; treat that as a normal revocation. ## Step 5 — Exchange There is no assertion-for-token exchange on the API-key paths. The key returned by `POST /v2/accounts` or created in GrowSurf Settings is the credential itself. Store it and move to Step 6. On the hosted MCP path, the MCP client performs the OAuth authorization-code exchange against the `token_endpoint` published in Authorization Server Metadata and holds the resulting access token. Let the client do this. Do not build the authorization request by hand, do not ask the user to paste an access token or refresh token, and do not fall back to an API key when an OAuth connection fails. GrowSurf does not implement the RFC 7523 `jwt-bearer` grant for agent identity assertions, so `grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer` with an `id-jag` assertion is not available. The grants GrowSurf actually supports are the ones listed in its Authorization Server Metadata document. ## Step 6 — Use the credential REST API requests use the key as a bearer credential: ```http GET /v2/campaigns HTTP/1.1 Host: api.growsurf.com Authorization: Bearer $GROWSURF_API_KEY Accept: application/json ``` Read the key from the approved secret source at call time. Do not place a literal key in source code, shell history, logs, issue descriptions, commit messages, screenshots, or model context. Redact authorization headers and credential fields from errors before showing them to the user. Never send a GrowSurf REST API key to a host other than `api.growsurf.com` or the official GrowSurf MCP server at `mcp.growsurf.com`. ### API-key scopes Choose the smallest scope set that can complete the task: | Scope | Access | | --- | --- | | `team:read` | Read the selected team's details and GrowSurf verification state. | | `team:write` | Update the team name or request and manage team verification. | | `api_key:rotate` | Rotate the key making a direct REST or SDK request. This scope and action are unavailable through MCP. | | `program:read` | Read programs, reward configuration, emails, installation, options, design, and webhooks. | | `program:write` | Create, clone, and update programs, and create, update, or delete reward configuration and webhooks. | | `participant:read` | Read participants, referrals, leaderboards, and participant activity. | | `participant:write` | Create and update participants, trigger or cancel referrals, and send participant emails or invites. | | `participant:delete` | Delete participants in one request or in bulk. | | `reward:read` | Read issued rewards, commissions, and payouts. | | `reward:write` | Record or refund sales and approve commissions or rewards without fulfilling them. | | `reward:delete` | Delete issued rewards or commissions. | | `reward:fulfill` | Fulfill an issued reward. This may deliver something of value. | | `analytics:read` | Read aggregate program and participant analytics. | A credential proves permission. It does not prove that the user authorized every action in the current conversation. Obtain the user's confirmation before destructive or irreversible actions, especially participant deletion, reward deletion, refunds, or reward fulfillment. The current OpenAPI document is the source of truth for endpoint requirements: `https://growsurf.com/openapi.json`. ## Errors | Status | Meaning | What to do | | --- | --- | --- | | `403` with `NOT_AUTHORIZED_ERROR`, message "No API key provided" | No `Authorization: Bearer` header reached the API. | Send the key you already hold. If it is no longer in reach, re-read the approved secret source. Do not ask for it in chat. | | `403` with `NOT_AUTHORIZED_ERROR`, message "not valid" | The key is unknown, revoked, or rotated. | Drop the cached value and ask the user to update the same secret source with the current key. | | `401` from hosted MCP | The OAuth token is missing, expired, or the connection was revoked. | Let the MCP client restart OAuth. Do not silently fall back to an API key. | | `403` with `EMAIL_NOT_VERIFIED_ERROR` | The team owner's email is not verified. | Ask the user to complete email verification, then retry the same request with the same key. | | Other `403` | The credential lacks a required scope or program grant. | Report the exact missing access. Do not broaden scopes or switch programs without the user's approval. | | `429` | The team or IP exceeded a rate or concurrency limit. | Back off and retry. Honor the `GrowSurf-Retry-After-*-Milliseconds` response headers when present. | | `503` from hosted MCP authentication | GrowSurf could not validate or exchange the credential safely. | Retry later. Do not bypass the failure with a weaker authentication path. | The REST API never returns `401`. Every credential problem on a REST API key is a `403` with `NOT_AUTHORIZED_ERROR`; read the message to tell a missing header from a dead key. `401` comes only from the hosted MCP endpoint's OAuth challenge. ## Revocation The user can revoke or replace API keys in GrowSurf Settings. Use one key per integration or agent so one credential can be revoked without disrupting unrelated systems. A key issued by `POST /v2/accounts` is replaced once, the first time the account owner signs in to the GrowSurf dashboard. Email verification does not trigger this, and an agent never triggers it. Treat it as a normal revocation when it happens: the old key returns `403` with `NOT_AUTHORIZED_ERROR`, and the owner collects the replacement from the dashboard. A direct REST or SDK client may rotate its own key through the API only when the current key has `api_key:rotate`. Rotation invalidates the current key and returns the replacement once. Rotate only when the user explicitly requests it, then update the approved secret source without exposing the replacement. OAuth clients should use the `revocation_endpoint` advertised in Authorization Server Metadata. Never send a REST API key to the OAuth revocation endpoint. If a credential appears in chat, logs, screenshots, source control, or another untrusted location, treat it as compromised and tell the user to revoke or rotate it immediately. ## Resources - Client-neutral agent onboarding: `https://growsurf.com/agent-start.md` - Authentication and REST API setup: `https://docs.growsurf.com/developer-tools/rest-api.md` - Safe onboarding workflow: `https://growsurf.com/arazzo.yaml` - Build with AI, SDK, and optional MCP setup: `https://docs.growsurf.com/build-with-ai.md` - MCP OAuth scope metadata: `https://mcp.growsurf.com/.well-known/oauth-protected-resource` - OpenAPI 3.1 document: `https://growsurf.com/openapi.json` - API key management: `https://app.growsurf.com/settings#api-keys` - Pricing: `https://growsurf.com/pricing/referral-programs/` and `https://growsurf.com/pricing/affiliate-programs/` - Terms of Service: `https://growsurf.com/terms` - Privacy Policy: `https://growsurf.com/privacy` - Support: `https://app.growsurf.com/settings#contact_support` For authentication or integration problems, contact GrowSurf Support. Never include credentials in a support request.