{"templateId":"GuidePage","sharedDataIds":{"sidebar":"sidebar-miniapps/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["callout"]},"type":"markdown"},"seo":{"title":"Salla","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":"salla","__idx":0},"children":["Salla"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An e-commerce platform with OAuth 2.0 auth, customer extraction, and order/cart handlers."]},{"$$mdtype":"Tag","name":"Callout","attributes":{"type":"warning","title":"Legacy block in snippet"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This example includes deprecated ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook.ecommerce"]}," for illustration. New submissions must use ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"/miniapps/guides/sync"},"children":["Sync"]}," (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sync.resources"]}," + ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sync.webhooks.handlers"]},")."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const sallaMiniApp = {\n  ns: 'salla',\n  name: 'salla',\n  type: 'salla',\n  title: 'Salla',\n  label: 'Salla',\n  description: 'Sync orders, customers, and abandoned carts from Salla.',\n  shortDescription: 'Salla ecommerce: orders, customers, carts',\n  status: 'public',\n  version: '1.0.0',\n  category: 'ecommerce',\n  icon: 'https://cdn.karzoun.com/miniapps/salla/icon.svg',\n  logo: 'https://cdn.karzoun.com/miniapps/salla/logo.svg',\n  docsUrl: 'https://docs.salla.dev',\n\n  auth: {\n    type: 'oauth2',\n    sensitiveKeys: ['accessToken', 'refreshToken', 'webhookSecret'],\n    config: {\n      accessToken: '',\n      refreshToken: '',\n      webhookSecret: '',\n    },\n    userDetails: {\n      url: 'https://api.salla.dev/admin/v2/oauth2/user/info',\n      method: 'GET',\n      headers: {\n        Authorization: 'Bearer [[accessToken]]',\n        'Content-Type': 'application/json',\n      },\n      mapping: { uid: '$.data.id', name: '$.data.name' },\n    },\n    get_token: {\n      url: 'https://accounts.salla.sa/oauth2/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: 'authorization_code',\n        code: '{{code}}',\n        redirect_uri: '{{redirect_uri}}',\n      },\n      mapping: {\n        accessToken: '$.access_token',\n        refreshToken: '$.refresh_token',\n        expiresIn: '$.expires_in',\n      },\n    },\n    refresh_token: {\n      url: 'https://accounts.salla.sa/oauth2/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      },\n    },\n    auto_refresh: true,\n    registrationRequests: [],\n  },\n\n  triggers: [\n    { type: 'miniapps:salla', event: 'order.created', icon: 'shopping-cart',\n      label: 'Order Created', description: 'New order placed', isCustom: false, img: 'automation3.svg' },\n    { type: 'miniapps:salla', event: 'order.status.updated', icon: 'shopping-cart',\n      label: 'Order Status Changed', description: 'Order status updated', isCustom: false, img: 'automation3.svg' },\n    { type: 'miniapps:salla', event: 'abandoned.cart', icon: 'shopping-cart',\n      label: 'Cart Abandoned', description: 'Customer abandoned their cart', isCustom: false, img: 'automation3.svg' },\n    { type: 'miniapps:salla', event: 'customer.created', icon: 'users-alt',\n      label: 'Customer Created', description: 'New customer registered', isCustom: false, img: 'automation3.svg' },\n    // ... more triggers\n  ],\n\n  actions: [],   // E-commerce apps typically have triggers only\n\n  source: {},\n\n  webhook: {\n    // Event name is in the body\n    eventExtraction: {\n      source: 'body',\n      path: '$.event',\n    },\n\n    // Dedup using data ID\n    transactionId: {\n      path: '$.data.id',\n      fallback: 'generate',\n    },\n\n    // HMAC-SHA256 verification\n    // Salla uses a global webhook secret (set in partner dashboard),\n    // so the secret is stored in auth.config.webhookSecret (not per-tenant)\n    verification: {\n      type: 'hmac-sha256',\n      headerName: 'X-Salla-Signature',\n      secretKey: 'webhookSecret',    // Resolves from auth.config (global secret)\n    },\n\n    // Acknowledge immediately\n    response: { statusCode: 200, body: { ok: true } },\n\n    // Auto-extract customer from webhook payload\n    customerExtraction: {\n      basePath: '$.data.customer',\n      mapping: {\n        firstName: 'first_name',\n        lastName: 'last_name',\n        primaryEmail: 'email',\n        primaryPhone: 'mobile',\n        code: 'id',\n      },\n      overrides: {\n        'customer.created': { basePath: '$.data' },\n      },\n    },\n\n    // E-commerce data processing\n    ecommerce: {\n      platform: 'salla',\n\n      // Pipeline stages\n      stageMappings: {\n        orders: {\n          type: 'static',\n          stages: [\n            { name: 'Pending', code: 'pending', externalStatusCode: 'pending', probability: '10', order: 0 },\n            { name: 'Processing', code: 'processing', externalStatusCode: 'processing', probability: '30', order: 1 },\n            { name: 'Shipped', code: 'shipped', externalStatusCode: 'shipped', probability: '60', order: 2 },\n            { name: 'Delivered', code: 'delivered', externalStatusCode: 'delivered', probability: '90', order: 3 },\n            { name: 'Completed', code: 'completed', externalStatusCode: 'completed', probability: '100', order: 4 },\n            { name: 'Cancelled', code: 'cancelled', externalStatusCode: 'cancelled', probability: '0', order: 5 },\n          ],\n        },\n        abandonedCarts: {\n          type: 'static',\n          stages: [\n            { name: 'Abandoned', code: 'abandoned', externalStatusCode: 'abandoned', probability: '10', order: 0 },\n            { name: 'Recovered', code: 'recovered', externalStatusCode: 'recovered', probability: '50', order: 1 },\n            { name: 'Completed', code: 'completed', externalStatusCode: 'completed', probability: '100', order: 2 },\n          ],\n        },\n      },\n\n      // Handler per webhook event\n      handlers: {\n        'order.created': {\n          recordType: 'order',\n          operation: 'create',\n          mapping: {\n            code: '$.data.id',\n            orderNumber: '$.data.reference_id',\n            status: '$.data.status.name',\n            totalAmount: '$.data.amounts.total.amount',\n            currency: '$.data.amounts.total.currency',\n            paymentMethod: '$.data.payment_method',\n            discountCode: { path: '$.data.discounts', transform: 'joinCodes' },\n            items: { path: '$.data.items', transform: 'mapItems' },\n          },\n        },\n\n        'order.status.updated': {\n          recordType: 'order',\n          operation: 'update',\n          mapping: {\n            code: '$.data.id',\n            status: '$.data.status.name',\n          },\n        },\n\n        'abandoned.cart': {\n          recordType: 'abandonedCart',\n          operation: 'create',\n          mapping: {\n            code: '$.data.id',\n            totalAmount: '$.data.total.amount',\n            currency: '$.data.total.currency',\n            checkoutUrl: '$.data.checkout_url',\n            state: 'abandoned',\n            items: { path: '$.data.items', transform: 'mapItems' },\n          },\n        },\n      },\n    },\n  },\n};\n","lang":"javascript"},"children":[]}]},"headings":[{"value":"Salla","id":"salla","depth":1}],"frontmatter":{"title":"Salla example","titleTranslationKey":"sidebar.miniapps.exampleSalla","audience":"developer","status":"published","locales":["en"],"template":"GuidePage","seo":{"title":"Salla"}},"lastModified":"2026-06-23T12:06:12.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/miniapps/examples/salla","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}