Authorization (OAuth)
Get a shop's approval and receive access tokens with the authorization code flow and PKCE.
The partner API uses the OAuth 2.1 authorization code flow. PKCE (S256) is required. Exchange codes for tokens on your server, where the client secret can stay secret.
Endpoints
| Purpose | URL |
|---|---|
| Authorization | https://api.toreca-cloud.com/api/auth/oauth2/authorize |
| Token | https://api.toreca-cloud.com/api/auth/oauth2/token |
| Revocation | https://api.toreca-cloud.com/api/auth/oauth2/revoke |
Resource (resource) |
https://api.toreca-cloud.com/partner/v1 |
| Authorization server metadata | https://api.toreca-cloud.com/api/auth/.well-known/oauth-authorization-server |
| Protected resource metadata | https://api.toreca-cloud.com/.well-known/oauth-protected-resource/partner/v1 |
Send resource=https://api.toreca-cloud.com/partner/v1 with every authorization, token and refresh request.
1. Open the authorization screen
Generate a random code_verifier and use its SHA-256 as the code_challenge. Generate a random state too and check it when the user comes back.
import { createHash, randomBytes } from "node:crypto";
const verifier = randomBytes(32).toString("base64url");
const challenge = createHash("sha256").update(verifier).digest("base64url");
const state = randomBytes(16).toString("hex");
const url = new URL("https://api.toreca-cloud.com/api/auth/oauth2/authorize");
url.search = new URLSearchParams({
response_type: "code",
client_id: process.env.TORECA_CLIENT_ID!,
redirect_uri: "https://lp.example.com/oauth/callback",
scope: "openid profile offline_access purchase-boosts:read",
state,
code_challenge: challenge,
code_challenge_method: "S256",
resource: "https://api.toreca-cloud.com/partner/v1",
}).toString();
// Save verifier and state in the session, then redirect to url
Include offline_access in scope. Without it no refresh token is issued and you’d need a new approval every 15 minutes. Asking for a scope the shop didn’t allow when registering the app fails with invalid_scope.
2. A shop member approves
The member signs in to Toreca Cloud, checks the shop and what is being allowed, and presses Allow. The browser returns to redirect_uri with code and state.
3. Exchange the code for tokens
const response = await fetch(
"https://api.toreca-cloud.com/api/auth/oauth2/token",
{
method: "POST",
headers: {
Authorization:
"Basic " +
Buffer.from(
`${encodeURIComponent(clientId)}:${encodeURIComponent(clientSecret)}`,
).toString("base64"),
"Content-Type": "application/x-www-form-urlencoded",
},
body: new URLSearchParams({
grant_type: "authorization_code",
code,
redirect_uri: "https://lp.example.com/oauth/callback",
code_verifier: verifier,
resource: "https://api.toreca-cloud.com/partner/v1",
}),
},
);
const token = await response.json();
// { access_token, refresh_token, token_type: "Bearer", expires_in: 900, scope }
4. Create the connection
With the tokens in hand, call POST /partner/v1/connection first. The response names the connected shop.
curl -X POST https://api.toreca-cloud.com/partner/v1/connection \
-H "Authorization: Bearer $ACCESS_TOKEN"
Every other call works only while this connection is active. See tokens and expiry for storing and refreshing tokens.

