{"templateId":"GuidePage","sharedDataIds":{"sidebar":"sidebar-miniapps/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Authentication","description":"Developer API, partner integration, MCP, SDK, and customer help center.","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"authentication","__idx":0},"children":["Authentication"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auth"]}," object tells the platform how users connect their accounts with your service."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"bearer-token-api-key","__idx":1},"children":["Bearer Token (API Key)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The simplest auth method. The user pastes their API key into a form, and the platform stores it securely."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"auth: {\n  type: 'bearer_token',\n\n  // Keys that must NEVER be sent to the frontend\n  sensitiveKeys: ['accessToken'],\n\n  // Default credential values (empty = user must provide)\n  config: {\n    accessToken: '',\n  },\n\n  // Verify the token works by calling a \"who am I\" endpoint\n  userDetails: {\n    url: 'https://api.example.com/users/me',\n    method: 'GET',\n    headers: {\n      Authorization: 'Bearer [[accessToken]]',\n      'Content-Type': 'application/json',\n    },\n    bodyType: 'json',\n    body: {},\n    mapping: {\n      uid: '$.user.id',        // Unique user identifier → metadata\n      name: '$.user.name',     // Display name → metadata\n    },\n  },\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["How it works:"]}]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["User enters their API key in the settings form"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Platform calls ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["saveMiniAppCredentials"]}," mutation"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["get_token"]}," is defined, it's executed to exchange/validate credentials"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Tokens and secrets are stored in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InstalledMiniApps.credentials"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userDetails"]}," is defined, mapped store/account fields are stored in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InstalledMiniApps.metadata"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Status set to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["connected"]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"per-tenant-install-storage","__idx":2},"children":["Per-tenant install storage"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each installed miniapp persists three server-side bags on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["InstalledMiniApps"]},":"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Contents"},"children":["Contents"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"[[key]]"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[[key]]"]}]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"{{key}}"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{{key}}"]}]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Exposed to UI?"},"children":["Exposed to UI?"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["credentials"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Tokens, secrets, registration outputs (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["accessToken"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhookId"]},", …)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Never"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["metadata"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Full ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userDetails.mapping"]}," result (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["storeId"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["storeName"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["uid"]},", …)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes (runtime)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Never"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userInput"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["User-entered install form values"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["No"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Yes"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Pre-fill only"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[[key]]"]}," resolves from ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["credentials first"]},", then ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["metadata"]}," (credentials win on key collision). Use camelCase keys in mappings (e.g. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["storeId"]},") even when the HTTP header name differs (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Store-Id: '[[storeId]]'"]},")."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"oauth-20","__idx":3},"children":["OAuth 2.0"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["For services that require OAuth 2.0 authorization (Salla, Google, etc.)."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"auth: {\n  type: 'oauth2',\n\n  sensitiveKeys: ['accessToken', 'refreshToken'],\n\n  // OAuth configuration (available as {{key}} placeholders)\n  config: {\n    client_id: 'your-client-id',\n    client_secret: 'your-client-secret',\n    scope: 'read write',\n    response_type: 'code',\n    grant_type: 'authorization_code',\n    accessToken: '',\n    refreshToken: '',\n  },\n\n  // Build the authorization redirect URL\n  auth_url: {\n    url: 'https://provider.com/oauth/authorize?client_id={{client_id}}&scope={{scope}}&response_type={{response_type}}&redirect_uri={{redirect_uri}}',\n    method: 'GET',\n    headers: {},\n    bodyType: 'json',\n    body: {},\n    mapping: {},\n  },\n\n  // Exchange authorization code for tokens\n  get_token: {\n    url: 'https://provider.com/oauth/token',\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    bodyType: 'json',\n    body: {\n      client_id: '{{client_id}}',\n      client_secret: '{{client_secret}}',\n      grant_type: '{{grant_type}}',\n      code: '{{code}}',\n      redirect_uri: '{{redirect_uri}}',\n    },\n    // Map response to credential keys\n    mapping: {\n      accessToken: '$.access_token',\n      refreshToken: '$.refresh_token',\n      expiresIn: '$.expires_in',\n    },\n  },\n\n  // Refresh expired tokens\n  refresh_token: {\n    url: 'https://provider.com/oauth/token',\n    method: 'POST',\n    headers: { 'Content-Type': 'application/json' },\n    bodyType: 'json',\n    body: {\n      client_id: '{{client_id}}',\n      client_secret: '{{client_secret}}',\n      grant_type: 'refresh_token',\n      refresh_token: '[[refreshToken]]',\n    },\n    mapping: {\n      accessToken: '$.access_token',\n      refreshToken: '$.refresh_token',\n      expiresIn: '$.expires_in',\n    },\n  },\n\n  // Auto-refresh on 401 responses\n  auto_refresh: true,\n\n  // Fetch user identity after authentication\n  userDetails: {\n    url: 'https://api.provider.com/v2/oauth2/user/info',\n    method: 'GET',\n    headers: {\n      Authorization: 'Bearer [[accessToken]]',\n      'Content-Type': 'application/json',\n    },\n    bodyType: 'json',\n    body: {},\n    mapping: {\n      uid: '$.data.id',\n      name: '$.data.name',\n    },\n  },\n\n  // Post-auth setup (see §4.4)\n  registrationRequests: [],\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["OAuth 2.0 Flow:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"User clicks \"Connect\" in Marketplace\n        │\n        ▼\n┌─────────────────────┐\n│ Browser popup opens  │\n│ GET /:appNs/oauth2   │\n└────────┬────────────┘\n         │\n         ▼\n┌─────────────────────┐\n│ Platform builds      │\n│ auth_url & redirects │\n│ to provider          │\n└────────┬────────────┘\n         │ User authorizes\n         ▼\n┌─────────────────────┐\n│ Provider redirects   │\n│ back with ?code=xxx  │\n└────────┬────────────┘\n         │\n         ▼\n┌─────────────────────┐\n│ Platform executes    │\n│ get_token request    │\n│ (code → tokens)      │\n└────────┬────────────┘\n         │\n         ▼\n┌─────────────────────┐\n│ Platform executes    │\n│ userDetails request  │\n│ (extract uid, name)  │\n└────────┬────────────┘\n         │\n         ▼\n┌─────────────────────┐\n│ Tokens stored in     │\n│ credentials;         │\n│ userDetails → metadata │\n│ registrationRequests   │\n│ executed (if any)      │\n└────────┬────────────┘\n         │\n         ▼\n┌─────────────────────┐\n│ Popup closes, main   │\n│ window refreshes     │\n└─────────────────────┘\n"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"token-refresh","__idx":4},"children":["Token Refresh"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auto_refresh: true"]}," is set and any action request returns a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["401"]}," response:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Platform automatically calls the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["refresh_token"]}," request"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["New credentials are extracted via the mapping"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Credentials are updated in the database"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The original failed request is retried with the new token"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is handled transparently — no user interaction required."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"registration-requests-post-auth-setup","__idx":5},"children":["Registration Requests (Post-Auth Setup)"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Registration requests are API calls executed ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["once"]}," right after a user successfully authenticates. Use them to:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Register webhooks"]}," with the provider"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fetch initial configuration"]}," (store ID, workspace info)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Subscribe to events"]}]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"auth: {\n  // ...auth config...\n\n  registrationRequests: [\n    // Register your webhook URL with the provider\n    {\n      url: 'https://api.provider.com/webhooks',\n      method: 'POST',\n      headers: {\n        Authorization: 'Bearer [[accessToken]]',\n        'Content-Type': 'application/json',\n      },\n      bodyType: 'json',\n      body: {\n        url: '{{webhookUrl}}',     // Auto-injected: your webhook endpoint\n        events: ['order.created', 'customer.updated'],\n        secret: '{{webhookSecret}}',\n      },\n      mapping: {\n        webhookId: '$.id',  // Saved to credentials for later reference\n      },\n    },\n\n    // Prefer auth.userDetails for store profile; use registrationRequests\n    // only for one-off setup outputs like webhook IDs.\n    {\n      url: 'https://api.provider.com/webhooks/subscribe',\n      method: 'POST',\n      headers: {\n        Authorization: 'Bearer [[accessToken]]',\n        'Store-Id': '[[storeId]]',   // Resolved from metadata\n      },\n      bodyType: 'json',\n      body: {\n        url: '{{webhookUrl}}',\n      },\n      mapping: {\n        webhookSubscriptionId: '$.id',\n      },\n    },\n  ],\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Available placeholders in registration requests:"]}]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Placeholder"},"children":["Placeholder"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Source"},"children":["Source"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[[accessToken]]"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Stored credentials"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The OAuth access token"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[[refreshToken]]"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Stored credentials"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The OAuth refresh token"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[[storeId]]"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Stored metadata"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Platform context from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userDetails"]}," (e.g. Zid)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[[any_key]]"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["credentials + metadata"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Any key from either bag (credentials win)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{{webhookUrl}}"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["System-generated"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Your miniapp's webhook endpoint URL"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{{subdomain}}"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["System context"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The tenant's subdomain"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{{uid}}"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["metadata / top-level ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["uid"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The authenticated user's ID"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{{configKey}}"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auth.config"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Any key from the auth config object"]}]}]}]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Important:"]}," Object mappings in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["registrationRequests"]}," are ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["persisted to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["credentials"]}]},". ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["userDetails"]}," mappings are ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["persisted to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["metadata"]}]}," in full. Both bags are available for ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[[key]]"]}," placeholders in sync headers, actions, and automations."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]}]},"headings":[{"value":"Authentication","id":"authentication","depth":1},{"value":"Bearer Token (API Key)","id":"bearer-token-api-key","depth":2},{"value":"Per-tenant install storage","id":"per-tenant-install-storage","depth":2},{"value":"OAuth 2.0","id":"oauth-20","depth":2},{"value":"Token Refresh","id":"token-refresh","depth":2},{"value":"Registration Requests (Post-Auth Setup)","id":"registration-requests-post-auth-setup","depth":2}],"frontmatter":{"title":"Authentication","titleTranslationKey":"sidebar.miniapps.authentication","audience":"developer","status":"published","locales":["en"],"template":"GuidePage","seo":{"title":"Authentication"}},"lastModified":"2026-06-23T12:06:12.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/miniapps/guides/authentication","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}