Sign in with Insy
Let Insy users grant your application access to their memberships and products using the OAuth 2.0 authorization code flow.
Sign in with Insy is an OAuth 2.0 authorization code flow. Your application sends a user to Insy, the user signs in and approves the scopes you asked for, and you receive an access token that lets you read that user’s data on their behalf.
Use OAuth when the data belongs to a user who is not you. Use an API key when your own backend acts on your own community.
Pick an integration
Login widget
One script tag, no backend. A public client that runs PKCE in the browser and hands you tokens through a callback or a DOM event.
Server-side flow
The full redirect and token exchange for a confidential client, where the secret and the tokens stay on your server.
PKCE
How to generate the verifier and the S256 challenge. Required for public clients, optional for confidential ones.
Scopes
The two scopes Insy grants, what each unlocks, and how to ask for the minimum.
Reference
Every OAuth endpoint, parameter, response shape and error code.
How the flow works
Send the user to Insy
Redirect the browser to GET https://api.insy.io/oauth/authorize with your client_id, a
registered redirect_uri, the scope you need and a state value you can verify later.
The user consents
Insy authenticates the user and shows a consent screen naming your application. If the user
declines you get redirected back with error=access_denied.
Insy redirects back with a code
Your redirect_uri receives ?code=...&state=.... The authorization code is valid for
60 seconds and can be exchanged once.
Exchange the code for tokens
POST https://api.insy.io/oauth/token returns an access token valid for one hour and a refresh
token valid for 30 days. The response status is 201, not 200.
Call the resource endpoints
Send Authorization: Bearer <access_token> to /oauth/userinfo, /oauth/memberships or
/oauth/products.
Register your application
Register your own client at Account → Developer → OAuth clients and press Create client. Credentials come back on the spot; nothing is provisioned by hand. The same screen edits, rotates and deletes a client afterwards.
What to fill in
| Item | Required | Notes |
|---|---|---|
| Application name | Yes | Shown to the user on the consent screen. |
| Client type | Yes | confidential or public — see below. |
| Redirect URIs | Yes | Exact URIs. Anything not registered is rejected. List every environment you use. One exception: widget clients do not register the hosted callback — https://insy.io/oauth/widget-callback is accepted automatically for any client that has at least one web origin registered. |
| Scopes | Yes | memberships.read, products.read, or both. Ask for the minimum. |
| Logo | Optional | Shown next to your application name on the consent screen. |
| Web origins | Widgets only | The origins the login widget may run on. See web origins. |
Confidential or public
The type is a property of the client, never inferred from a request — but you can change it on the client’s own screen if you picked wrong.
| Confidential | Public | |
|---|---|---|
Has a client_secret |
Yes | No |
| Where it runs | Your server | A browser or a native app |
client_secret in the token request |
Required | Must be omitted — sending one returns invalid_client |
| PKCE | Optional | Required |
Choose confidential if you have a backend that can hold a secret. Choose public if the flow completes in a browser or a mobile app, including anything built on the login widget.
What you get back
| Credential | Format | Issued to |
|---|---|---|
client_id |
oac_ followed by 24 hex characters |
Every client |
client_secret |
ocs_ followed by 48 hex characters |
Confidential clients only |
The client_id is not a secret. It appears in browser URLs and, for widgets, in your page source.
Token lifetimes
| Token | Lifetime | Behaviour |
|---|---|---|
| Authorization code | 60 seconds | Single use. Exchange it immediately. |
| Access token | 1 hour (expires_in: 3600) |
Opaque string. Do not parse it. |
| Refresh token | 30 days, sliding | Each refresh issues a new pair with a fresh 30-day window. |
Refreshing revokes the old refresh token and the old access token immediately. Replace both in
storage as a single atomic write, or an in-flight request will start failing with invalid_token.
Known deviations from the RFCs
The token endpoint returns 201, not 200RFC 6749 §5.1 mandates 200
POST /oauth/token answers with HTTP 201 on success. Strict client libraries assert 200 and
will throw on an otherwise valid response. Either configure the library to accept 201 or make
the token request with plain HTTP.
HTTP Basic client authentication is not supported
Credentials go in the request body (client_secret_post). client_secret_basic — the
default in most libraries — is not accepted. Set the token endpoint authentication method
explicitly.
scope is required on the authorize request
RFC 6749 makes scope optional. Insy requires it. A request without scope will not produce a
code.
No WWW-Authenticate header on 401 or 403
Resource endpoints return the error as a JSON body only. Libraries that discover the error from
the WWW-Authenticate header will see nothing. Read the response body.
No refresh token reuse detection
Replaying a revoked refresh token fails, but it does not invalidate the rest of the token family. Treat refresh tokens as high-value secrets on your side.
There are no official SDKs
Every example in these docs is plain HTTP against https://api.insy.io. There is no sandbox
environment and no version prefix in the URL.
Response shapes
The OAuth token, revoke, userinfo, memberships and products endpoints return bare, RFC-shaped JSON —
not the BaseResponse envelope used elsewhere in the API.
{
"access_token": "…",
"refresh_token": "…",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "memberships.read"
}
{
"error": "invalid_grant",
"error_description": "Authorization code has expired"
}
The one exception is GET /oauth/client/{clientId}, the public endpoint for rendering your own
consent screen, which does use the envelope.