Skip to content

Authentication

Request, use, and renew OAuth tokens.

Auth issues OAuth 2.0 tokens at https://auth.ecomiq.pe/connect/token. Send the token in the Authorization: Bearer header.

Your server
Auth
Admin API
POST /connect/token
200 with access_token and expires_in
GET /v1/catalog/products with Bearer
200 with the data
When it expires, request a new token
The secret only travels to Auth, never to the API.

Application credentials

An application created in the dashboard gives you a client_id and a client_secret. The secret lives only on your server, never in a browser or a distributed app.

API keys created with POST /v1/api-keys are not OAuth tokens: Admin, Storefront, and Auth reject them.

Token for an Admin integration

Use client_credentials with the api_admin scope:

curl -X POST https://auth.ecomiq.pe/connect/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=$ECOMIQ_CLIENT_ID" \
  -d "client_secret=$ECOMIQ_CLIENT_SECRET" \
  -d "scope=api_admin"
Response
{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6...",
  "token_type": "Bearer",
  "expires_in": 3600
}

expires_in is the lifetime in seconds.

Use the token

curl "https://api.ecomiq.pe/api/v1/catalog/products?limit=25" \
  -H "Authorization: Bearer $ECOMIQ_TOKEN"

The store and, when it applies, the seller come from the token. See Organization, store, and seller.

Grants

GrantUse
client_credentialsA server acts as the application. Requires a confidential client.
authorization_codeA person authorizes the application. See User authorization.
refresh_tokenRenews a session, if the application has this grant.

Each application accepts only the grants and scopes configured for it.

Renewal

client_credentials has no refresh token: request a new one before it expires. Cache the token with a one-minute margin.

token-cache.ts
let cached: { token: string; expiresAt: number } | undefined;

export async function getToken(): Promise<string> {
  if (cached && Date.now() < cached.expiresAt - 60_000) {
    return cached.token;
  }

  const body = new URLSearchParams({
    grant_type: "client_credentials",
    client_id: process.env.ECOMIQ_CLIENT_ID!,
    client_secret: process.env.ECOMIQ_CLIENT_SECRET!,
    scope: "api_admin",
  });

  const response = await fetch("https://auth.ecomiq.pe/connect/token", {
    method: "POST",
    headers: { "Content-Type": "application/x-www-form-urlencoded" },
    body,
  });

  if (!response.ok) throw new Error(`OAuth HTTP ${response.status}`);

  const payload = await response.json();
  cached = {
    token: payload.access_token,
    expiresAt: Date.now() + payload.expires_in * 1000,
  };

  return cached.token;
}

If several processes share credentials, coordinate renewal so they don't request tokens in a burst.

Authentication failures

  • 401: the endpoint did not receive a valid credential.
  • 403: the credential is valid, but a scope, permission, or context blocks it.
  • /connect/token returns OAuth errors such as invalid_client or invalid_grant. See Errors.

Don't log tokens, secrets, or full OAuth responses.

Customer sign-in

Chosen in Settings → Checkout → Customer accounts → Sign-in (checkoutSettings.general.customerLoginMode):

ModeForm
hostedAuth page with the store's name and logo. Default.
storefrontInside the store, styled by the theme.

In both modes Auth verifies the email and code. Switching modes keeps the accounts. A customer and an Admin user can share an email address.

  1. The store server configures the OAuth client with POST /v1/customer/auth/config and receives loginMode, captcha, and the OAuth settings.
  2. In storefront mode, it requests the code with POST /v1/customer-auth/challenges.
  3. The SDK exchanges the code (Authorization Code with PKCE) and stores the session in a secure cookie. The theme never stores tokens.

CAPTCHA is optional: Cloudflare Turnstile or Google reCAPTCHA v2. Attempt limits always apply.

Registration details

Chosen in Settings → Checkout → Registration details (checkoutSettings.general.customerRegistration). firstName, lastName, phone, and dateOfBirth can be hidden (not asked), optional, or required. The default is hidden.

  1. After email verification, the server reads GET /v1/customer/registration: fields, values, and requiresCompletion.
  2. When requiresCompletion is true, the theme shows the form before the account or checkout.
  3. The server sends the details to POST /v1/customer/registration. If every field is optional, {} is enough.

Rules:

  • While registration is pending, account APIs and order creation return Customer.RegistrationRequired.
  • The server always validates: hiding a field in the theme doesn't skip it.
  • An existing customer sees the form again only when a required field is missing.
  • dateOfBirth can't be later than today in the store.
  • PATCH /v1/customer can't clear a required field.
  • The welcome email is sent once, when registration completes.
  • Guest checkout is a separate setting.

Last updated:

On this page