OAuth for apps
Authorize an app to act on a user's Clipform workspace.
OAuth is for apps other people install - the Clipform apps for Zapier and Make use it. Your users click Connect, sign in to Clipform, pick a workspace, and approve; you never handle their password and they can revoke you at any time.
Building a private integration for your own workspace? Use an API key instead - it is simpler and does the same job.
client_id and client_secret.Endpoints
| Purpose | Endpoint |
|---|---|
| Authorization | https://api.clipform.io/oauth/authorize |
| Token + refresh | https://api.clipform.io/oauth/token |
| Revocation (RFC 7009) | https://api.clipform.io/oauth/revoke |
These are separate from the MCP server at mcp.clipform.io, whose tokens are audience-bound to MCP. Tokens issued here carry no audience restriction and authorize the REST API at api.clipform.io.
Scopes
| Scope | Grants |
|---|---|
forms:read | Read the workspace's forms |
responses:read | Read the workspace's form responses |
webhooks:manage | Subscribe and unsubscribe webhook endpoints |
Request only what you use. Scopes are space-separated in the scope parameter.
The flow
1. Send the user to authorize
https://api.clipform.io/oauth/authorize
?client_id=your_client_id
&redirect_uri=https://your-app.example/callback
&response_type=code
&scope=forms:read%20responses:read%20webhooks:manage
&state=random_valuePublic clients (no secret) must also send code_challenge and code_challenge_method=S256 (PKCE). Clipform redirects back with ?code=...&state=....
2. Exchange the code for tokens
curl -X POST https://api.clipform.io/oauth/token \
-d grant_type=authorization_code \
-d code=the_code \
-d redirect_uri=https://your-app.example/callback \
-d client_id=your_client_id \
-d client_secret=your_client_secret{
"access_token": "cf_at_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "cf_rt_...",
"scope": "forms:read responses:read webhooks:manage",
"workspace_name": "Acme Marketing"
}3. Call the API
curl -H "Authorization: Bearer cf_at_your_token" \
https://api.clipform.io/v1/connect/meGet the connected workspace is the quickest way to confirm a connection works - it returns the workspace_id and workspace_name the token is bound to, which makes a good connection label.
Refreshing
Access tokens last 1 hour; refresh tokens last 90 days and rotate on every use, so store the new one each time.
curl -X POST https://api.clipform.io/oauth/token \
-d grant_type=refresh_token \
-d refresh_token=cf_rt_your_token \
-d client_id=your_client_id \
-d client_secret=your_client_secretRefresh before the access token expires. An expired access token is rejected as an ordinary 401, not as a distinct "token expired" signal, so a platform that only refreshes reactively will see hard failures.
Reusing an already-rotated refresh token is treated as a possible theft (RFC 9700) and revokes the whole token family, forcing the user to reconnect.
Errors
Token endpoint errors follow RFC 6749:
{ "error": "invalid_grant", "error_description": "Invalid or expired refresh token" }The REST API uses Clipform's own shape:
{ "code": "UNAUTHORIZED", "error": "Invalid or expired token" }What you can call
The Integrations endpoints are the connect lane an installed app uses: identify the workspace, list its forms, and subscribe to form completions. Deliveries follow the standard webhook payload.
Users manage and revoke your app's access from Settings → Connected apps in their dashboard.