OAuth
Authorize an integration to access a customer's SnitchFeed organization on their behalf.
Integrations can access a customer's SnitchFeed data with OAuth 2.0 instead of asking the customer for an API key. The customer authorizes your integration, picks which organization to grant, and your integration gets short-lived access tokens scoped to just that organization and the permissions they approved.
Use OAuth when you're building an integration other customers will install. Use an API key when you're calling the API for your own single organization.
OAuth clients are provisioned by SnitchFeed. There is no self-serve client registration. Contact us to register your integration and get a client_id / client_secret.
Endpoints
All OAuth protocol endpoints are on the SnitchFeed API origin (https://api.snitchfeed.com), under /auth. Login, org selection, and consent UI run on the app (https://app.snitchfeed.com).
| Method | Path | Description |
|---|---|---|
GET | /auth/oauth2/authorize | Start the authorization flow |
POST | /auth/oauth2/token | Exchange a code (or refresh token) for an access token |
POST | /auth/oauth2/revoke | Revoke a token (RFC 7009) |
GET | /auth/.well-known/oauth-authorization-server | Discovery metadata |
Discovery: https://api.snitchfeed.com/auth/.well-known/oauth-authorization-server
Scopes
| Scope | Grants |
|---|---|
mentions:read | GET /v1/mentions, /v1/mentions/{id}, /v1/mentions/count |
listeners:read | GET /v1/listeners, /v1/listeners/{id} |
feeds:read | GET /v1/feeds, /v1/feeds/{id} |
org:read | GET /v1/organization/context, /v1/organization/usage |
offline_access | Issues a refresh token, so your app can stay connected until the customer revokes access |
Request only the scopes your integration needs. They're shown to the customer on the consent screen. A client can only request scopes it was registered with; SnitchFeed enforces that ceiling at authorization time.
Tokens and org scope
- Access tokens expire after 1 hour. Refresh tokens (if you requested
offline_access) are valid for 30 days; authorization codes expire after 10 minutes, so exchange them immediately. - Each authorization grants access to exactly one organization: the one the customer picks at
/oauth/select-orgon the app. A token cannot span multiple organizations. To connect a second organization for the same customer, run the authorize flow again; they'll get another org picker. - There's no sandbox environment. Integrations are built and tested directly against production. Use a test organization if you don't want to touch real data while developing.
Authorize a customer
-
Redirect the customer to
/auth/oauth2/authorizewith:Param Value client_idYour client id redirect_uriMust exactly match one of your registered redirect URIs, including scheme and path. Registered URIs must be https://(http://is only accepted forlocalhost/127.0.0.1during local development)response_typecodescopeSpace-separated scopes, e.g. mentions:read feeds:read offline_accessresourcehttps://api.snitchfeed.com/v1(RFC 8707 audience)stateOpaque value you'll verify on callback code_challenge,code_challenge_method=S256PKCE (required for all clients) -
The customer logs in (if needed), picks which of their organizations to connect, and approves the requested scopes on the consent screen.
-
SnitchFeed redirects back to your
redirect_uriwithcodeand yourstate.
GET https://api.snitchfeed.com/auth/oauth2/authorize
?client_id=YOUR_CLIENT_ID
&redirect_uri=https://yourapp.com/oauth/callback
&response_type=code
&scope=mentions:read%20feeds:read%20offline_access
&resource=https://api.snitchfeed.com/v1
&state=RANDOM_OPAQUE_STATE
&code_challenge=YOUR_CODE_CHALLENGE
&code_challenge_method=S256Exchange the code for a token
curl -sS -X POST "https://api.snitchfeed.com/auth/oauth2/token" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d "grant_type=authorization_code" \
-d "code=THE_CODE_FROM_CALLBACK" \
-d "redirect_uri=https://yourapp.com/oauth/callback" \
-d "code_verifier=YOUR_PKCE_VERIFIER"Client authentication is HTTP Basic (client_secret_basic) only. Send your client id and secret via -u/Authorization: Basic, not as body params.
{
"access_token": "...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "...",
"scope": "mentions:read feeds:read offline_access"
}Call the API the same way you would with an API key. See Authentication:
curl -sS "https://api.snitchfeed.com/v1/organization/context" \
-H "Authorization: Bearer THE_ACCESS_TOKEN"Access tokens are opaque (not JWTs) and checked against SnitchFeed's servers on every request, so a revoked token stops working immediately. There's no caching lag to account for.
Refreshing
If you requested offline_access, exchange the refresh token for a new access token the same way:
curl -sS -X POST "https://api.snitchfeed.com/auth/oauth2/token" \
-u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
-d "grant_type=refresh_token" \
-d "refresh_token=YOUR_REFRESH_TOKEN"Rate limits
/auth/oauth2/authorize and /auth/oauth2/token are not rate limited. Once you're calling /v1/* with an access token, the usual per-organization rate limits apply (same limits as an API key, since they're enforced per organization rather than per credential).
Revoking access
Either side can end the connection:
- Your app can revoke a token it holds via
POST /auth/oauth2/revoke(token, plus your client credentials). - The customer can disconnect your app any time from their SnitchFeed workspace's connected apps list, without involving you.
If you registered a revoke webhook URL, SnitchFeed notifies your app when a customer disconnects so you can clean up stored tokens on your side. Deliveries are signed with X-SnitchFeed-Signature (HMAC-SHA256) and deduplicated with X-SnitchFeed-Delivery-Id.
Errors
These use the standard OAuth2 error / error_description shape, not the /v1 error envelope (that only applies to data API calls). The two OAuth endpoints deliver errors differently:
/auth/oauth2/authorize: once yourclient_idandredirect_uriare recognized, errors (access_denied,invalid_scope,invalid_request, etc.) come back as a redirect to yourredirect_uriwitherror,error_description, andstateas query params. There's no JSON body to read. Ifclient_idorredirect_uriitself is invalid or unregistered, SnitchFeed can't safely redirect and shows an error page instead./auth/oauth2/token: errors are a JSON body with HTTP 400 (including for bad client credentials;invalid_clientis 400 here, not 401). Common cases:invalid_grant(code, refresh token, or PKCE verifier didn't match, or the code already expired/was used),invalid_scope(requested a scope your client isn't registered for),invalid_client(bad client id/secret).
Once you're calling /v1/* with an access token, a revoked or expired token returns the usual 401 unauthorized from the data API.