Skip to content
トレカクラウド
Esc
↑↓navigate↵open⌘Jpreview
On this page

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.

Last updated on October 4, 2026

Was this page helpful?