Developers

OAuth 2.0 with PKCE

Authorization code flow with mandatory PKCE (S256): authorize URL, consent, callback, token exchange, refresh, revoke and every error.

ColiSwift implements the OAuth 2.0 authorization code grant (RFC 6749) with PKCE S256 (RFC 7636) required for every client, refresh-token rotation, and token revocation (RFC 7009). No other grant type exists: no client credentials, no implicit, no password grant.

LifetimeValue
Authorization code5 minutes, single use
Access token (cs_at_…)expires_in seconds — 3600 (1 hour) by default
Refresh token (cs_rt_…)90 days by default, single use (rotated on every refresh)

1. Authorize URL

GEThttps://coliswift.com/oauth/authorizeBrowser — the seller signs in
Query parameters
response_typestringrequired

Only code is supported.

  • code
client_idstringrequired

Your cs_client_… id.

redirect_uristringrequired

Must be byte-for-byte one of your registered URIs. A trailing slash makes it a different URI. There is no default.

scopestring

Space-separated scopes. Omitted = every scope your app is allowed. Present = exactly those; an unknown scope or one your app is not allowed is refused (never silently dropped).

statestringrequired

Your random anti-CSRF value; returned unchanged. Required and non-empty.

code_challengestringrequired

BASE64URL(SHA256(code_verifier)), no padding (43–128 chars of A-Z a-z 0-9 - . _ ~).

code_challenge_methodstringrequired

plain is refused.

  • S256

The verifier is 43–128 characters of the same unreserved set; 32 random bytes in base64url (43 characters) is the usual choice. Code to generate both is in the Quickstart.

  • A signed-out visitor is sent to the ColiSwift login and back to the consent page with the request intact.
  • Only an active seller account can approve. A staff account sees « Only a seller account can connect an application » (OAUTH_NOT_A_SELLER); so does a seller whose account is not active.
  • The page shows your app’s name and logo, the seller’s identity and one sentence per requested scope. If the seller already connected your app it says so.
  • The seller clicks approve or deny.

A malformed request is answered on the ColiSwift page and the browser is not sent back to you — redirecting to an unverified address would make the endpoint an open redirector. Fix the link if you see one of these while testing:

CodeCause
OAUTH_INVALID_CLIENTclient_id missing, unknown, or the app is deactivated.
OAUTH_INVALID_REDIRECT_URIredirect_uri missing or not exactly registered.
OAUTH_UNSUPPORTED_RESPONSE_TYPEresponse_type is not code.
OAUTH_INVALID_REQUESTstate missing; code_challenge_method not S256; code_challenge missing or malformed.
OAUTH_INVALID_SCOPEAn unknown scope, an empty scope=, or a scope outside your app’s allowed list.
OAUTH_NOT_A_SELLERSigned in as staff, or the seller account is not active.
OAUTH_APP_PRIVATEA sandbox developer app opened by a seller other than the one who created it. Only that account can connect it; nothing is shared.

3. Callback

After the decision the browser lands on your redirect_uri:

GET https://app.example.com/coliswift/callback?code=cs_code_…&state=YOUR_STATE

Always compare state with the value in the user’s session before doing anything else.

Connecting the same seller again

There is one connection (grant) per app and seller. A second approval reuses it: nothing is revoked, existing tokens keep working, and the grant’s scopes become the union of the old and the new ones. The tokens minted from the new code carry the scopes of that consent; tokens obtained later by refresh carry the grant’s full set.

4. Exchange the code

POSThttps://coliswift.com/api/oauth/tokenClient id + secret

Body application/x-www-form-urlencoded (a JSON body is also read). Authenticate with client_id + client_secret in the body or HTTP Basic (Authorization: Basic base64(client_id:client_secret)); Basic wins when both are sent.

grant_typestringrequired
  • authorization_code
codestringrequired

The cs_code_… from the callback.

redirect_uristringrequired

The same exact URI used in the authorize request.

code_verifierstringrequired

The verifier whose S256 hash you sent as code_challenge.

curl -X POST "https://coliswift.com/api/oauth/token" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=$CODE" \
  --data-urlencode "redirect_uri=https://app.example.com/coliswift/callback" \
  --data-urlencode "code_verifier=$CODE_VERIFIER" \
  --data-urlencode "client_id=$COLISWIFT_CLIENT_ID" \
  --data-urlencode "client_secret=$COLISWIFT_CLIENT_SECRET"
200 OK
{
  "access_token": "cs_at_…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "cs_rt_…",
  "scope": "profile:read catalogue:read orders:write orders:read",
  "seller_id": "6a1c2f0e-3b7d-4c55-9e10-2f8a4b7c9d01",
  "seller_name": "Atlas Beauty"
}
Response
access_tokenstring

cs_at_…. Send as Authorization: Bearer … on every API call.

token_typestring
  • Bearer
expires_ininteger

Seconds until the access token expires.

refresh_tokenstring

cs_rt_…. Single use.

scopestring

Space-separated scopes this token carries.

seller_idstring

The ColiSwift seller this connection belongs to. Use it as your key; it is also seller_id in every webhook envelope.

seller_namestring

Company name, else first + last name.

Responses carry Cache-Control: no-store. Tokens are opaque: do not parse them.

5. Refresh

Same endpoint, grant_type=refresh_token. Every refresh returns a new pair; the refresh token you sent is spent. Store the new one before using the new access token.

curl -X POST "https://coliswift.com/api/oauth/token" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token=$REFRESH_TOKEN" \
  --data-urlencode "client_id=$COLISWIFT_CLIENT_ID" \
  --data-urlencode "client_secret=$COLISWIFT_CLIENT_SECRET"

6. Revoke (disconnect)

POSThttps://coliswift.com/api/oauth/revokeClient id + secret

Send token = an access or a refresh token. The whole connection for that seller is revoked: every access and refresh token of the grant dies. The answer is always 200 {} — also for an unknown token, a token of another app or a wrong secret — so it never confirms which tokens exist.

curl -X POST "https://coliswift.com/api/oauth/revoke" \
  --data-urlencode "token=$REFRESH_TOKEN" \
  --data-urlencode "client_id=$COLISWIFT_CLIENT_ID" \
  --data-urlencode "client_secret=$COLISWIFT_CLIENT_SECRET"

A seller can also disconnect your app from their ColiSwift Integrations page, and ColiSwift revokes every connection when an app is deactivated. You are not notified by webhook: your next API call answers 401 INVALID_TOKEN, and the refresh answers invalid_grant. Treat both as « the seller must connect again ».

While a seller’s ColiSwift account is deactivated, every API call with their tokens answers 401 INVALID_TOKEN and no event is sent for them. The connection itself is not revoked: if ColiSwift reactivates the account, the connection works again.

Token endpoint errors

The token and revoke endpoints answer in the RFC 6749 §5.2 shape, not the API’s error envelope:

400 Bad Request
{
  "error": "invalid_grant",
  "error_description": "The authorization code has expired."
}
HTTPerrorWhen
401invalid_clientMissing, unknown or wrong client credentials, or the app is deactivated (same message for every case). With Basic auth a WWW-Authenticate: Basic realm="oauth" header is added.
400invalid_requestcode, redirect_uri or code_verifier missing; refresh_token missing.
400invalid_grantCode or refresh token unknown, expired, already used, issued to another app; grant revoked; redirect_uri different from the authorize one; verifier does not match the challenge.
400unsupported_grant_typeAnything but authorization_code or refresh_token.
429—More than 60 requests per minute for one client id from one address. This one uses the API envelope {"error":{"code":"RATE_LIMITED",…}}.