# Get Started

Get started with App Functions in minutes. This guide walks you through creating your first App Function that responds to store events.

## Prerequisites

Before you begin, make sure you have:

- ✔️ **Salla Partner Account** — [Sign up here](https://portal.salla.partners) if you don't have one
- ✔️ **An App Created** — Create an app in the Salla Partner Portal
- ✔️ **Demo Store** — Install your app on a demo store for testing
- ✔️ **App Scopes** — Configure the necessary scopes for the events you want to listen to

---


## Setup Your Environment

### 1. Configure App Scopes

App scopes determine which events your app can access. To configure scopes:

<Steps>
  <Step title="Open Partner Portal">
    Navigate to your app in the [Salla Partner Portal](https://portal.salla.partners)
  </Step>

  <Step title="Configure Scopes">
    Scroll down to the **App Scopes** section and select the scopes needed for your App Functions (e.g., `orders.read`, `products.read`)
  </Step>

  <Step title="Save Changes">
    Click **Save** to apply your scope configuration
  </Step>
</Steps>

![App Scopes Configuration](https://api.apidog.com/api/v1/projects/451700/resources/366873/image-preview)


<Info>
Read more about Webhooks, App Scopes, and Events in the [Salla Documentation](https://docs.salla.dev/421119m0).
</Info>

---

### 2. Install on Demo Store

To test your App Functions, install your app on a demo store:

<Steps>
  <Step title="Navigate to Your App">
    In the Partner Portal, open your app dashboard
  </Step>

  <Step title="Install on Demo Store">
    Click **Install on Demo Store** and select or create a demo store
  </Step>

  <Step title="Complete Installation">
    Follow the installation wizard to complete the process
  </Step>
</Steps>

![Install on Demo Store](https://api.apidog.com/api/v1/projects/451700/resources/366874/image-preview)



<TipGood>Learn more about [testing with demo stores](https://salla.dev/blog/how-to-test-your-app-using-salla-demo-stores/).</TipGood>

---

## Understanding the Context Object

Before creating your first function, it's important to understand what data your function receives.

### How App Functions Work

```mermaid
graph LR
    A[Event Occurs] --> B[Salla Platform]
    B --> C[Context Object]
    C --> D[Your Function]
    D --> E[Response]

    style A fill:#e3f2fd
    style B fill:#fff3e0
    style C fill:#f3e5f5
    style D fill:#e8f5e9
    style E fill:#fce4ec
```

**The Flow:**

1. ⚡ **Event Occurs** — Merchant creates order, customer views product, etc.
2. 🔄 **Salla Platform** — Detects the event and finds your App Function
3. 📦 **Context Object** — Wraps event data (payload) + your app settings
4. 💻 **Your Function** — Receives context and executes your logic
5. ✔️ **Response** — Returns result (affects action for sync, ignored for async)

---

### What You Receive

Every App Function receives a **context object** with three main parts:

```javascript Basic Structure
export default async (context: ContextType): Promise<Resp> {
  const { payload, settings, merchant } = context;

  // payload - The webhook event data from Salla
  // settings - Your app configuration from Partner Portal
  // merchant - Merchant details object
}
```

```typescript TypeScript
export default async (context: ContextType): Promise<Resp> {
  const { payload, settings, merchant } = context;

  // Full type safety and autocomplete
  const orderId = payload.data.id;
  const merchantId = merchant.id;
}
```

---

### The Payload Object

The `payload` contains the event data that Salla sends:

| Field | Type | Description |
|-------|------|-------------|
| `event` | `string` | Event name (e.g., `order.created`, `product.updated`) |
| `merchant` | `number` | Merchant ID who installed your app |
| `created_at` | `string` | ISO timestamp when the event occurred |
| `data` | `object` | Event-specific data (order, product, customer, etc.) |

**Example:**

```json
{
  "event": "order.created",
  "merchant": '123456',
  "created_at": "2024-03-24T10:30:00Z",
  "data": {
    "id": 789,
    "status": "pending",
    "total": 299.99
  }
}
```

> The payload is diffrenet for each event/action, you can find the schema of each event/action in [Merchants Events](https://docs.salla.dev/5460616f0.md) or [Customers Events](https://docs.salla.dev/5460615f0.md) pages

---

### The Settings Object

The `settings` contains your app's configuration values that you define in the Partner Portal:

```json
{
  "apiKey": "your-api-key",
  "webhookUrl": "https://api.example.com/webhook",
  "syncEnabled": true
}
```

<TipInfo>Each merchant can customize these settings when they install your app.</TipInfo>

---

## Creating Your First App Function

Let's create a simple App Function that listens to order status updates and sends data to an external webhook.

---

### Step 1: Access the App Functions Editor

<Steps>
  <Step title="Login to Partner Portal">
    Log in to your [Salla Partner Portal](https://portal.salla.partners/login)
  </Step>

  <Step title="Open Your App">
    Navigate to the app you want to add the function to
  </Step>

  <Step title="Find App Functions Section">
    Scroll down to the **App Functions** section
  </Step>

  <Step title="Add New Function">
    Click **Add New Function** to open the editor
  </Step>
</Steps>

![Add New Function](https://api.apidog.com/api/v1/projects/451700/resources/366875/image-preview)

The App Function builder will appear with these sections:

- 🏷️ **Function Name** — Name your function
- 📋 **Action Selector** — Choose which event triggers your function
- 💻 **Code Editor** — Write your function logic
- 👁️ **Preview Panel** — Test and view results

![App Function Builder](https://api.apidog.com/api/v1/projects/451700/resources/366876/image-preview)

---

### Step 2: Name Your Function

Enter a descriptive name for your function that clearly indicates its purpose.

**Example:** `order-status-webhook-notifier`

**Best Practices:**

- ✔️ Use lowercase with hyphens
- ✔️ Be descriptive and specific
- ✔️ Include the event type and action

---

### Step 3: Select an Action

Click on the **Select Action** dropdown to see all available actions and events.

![Action Selector](https://api.apidog.com/api/v1/projects/451700/resources/365162/image-preview)

For this example, select **Order Status Updated** from the list.

**Available Event Categories:**

| Category | Examples |
|----------|----------|
| **Orders** | created, updated, cancelled, refunded |
| **Products** | added, updated, deleted, quantity low |
| **Customers** | created, updated, login, OTP request |
| **Shipments** | creating, created, cancelled, updated |
| **And many more...** | brands, categories, coupons, reviews |

![Order Status Selected](https://api.apidog.com/api/v1/projects/451700/resources/366877/image-preview)

---

### Step 4: Write Your Function Code

Once you select an action, the code editor updates with the function signature. Now you can write your custom logic.

**Example Function**: Send order status updates to a webhook


```typescript Full Example
export default async (context: OrderStatusUpdated): Promise<Resp> => {
  // Destructure context for clarity
  const { payload, settings, merchant } = context;
  const { data: order, event, } = payload;

  // 1. Prepare payload for the external webhook service
  const webhookPayload = {
    order_id: order.id,
    event: event,
    order_status: order.status,
    merchant_id: merchant.id,
    timestamp: new Date().toISOString(),
    message: "Order status updated successfully"
  };

  // 2. Call the external webhook URL defined in the app settings
  // The API key is also stored in settings and customized by the merchant
  const response = await fetch(settings.webhookUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${settings.webhookApiKey}` // Key from app settings
    },
    body: JSON.stringify(webhookPayload)
  });

  // 3. Return a consistent response based on the webhook call status
  if (!response.ok) {
    await sendEmail({
        to: payload.data.customer.email,
        subject: `Order ${payload.data.id} Confirmed`,
        body: `Thank you for your order!`
      });
  }

  return Resp.success().setData({
      webhook_status: response.status,
      order_id: order.id
  });
}
```

**Key Points:**

- ✔️ The function receives a `context` object with `payload`, `settings` and `merchant` objects
- ✔️ Use `async/await` for asynchronous operations
- ✔️ Always return a response object with `success` status

---

### Step 5: Test Your Function

Before deploying, test your function using the preview feature.

<Steps>
  <Step title="Select Demo Store">
    Click **Select Store** and choose your demo store
      
    ![Select Store](https://api.apidog.com/api/v1/projects/451700/resources/366878/image-preview)
  </Step>

  <Step title="Get Test Data">
    Navigate to your demo store dashboard and get test data (e.g., an Order ID)
      
    ![Demo Store Dashboard](https://api.apidog.com/api/v1/projects/451700/resources/366879/image-preview)
  </Step>

  <Step title="Enter Test Parameters">
    Enter the Order ID in the preview panel
      
    ![Enter Order ID](https://api.apidog.com/api/v1/projects/451700/resources/366880/image-preview)
  </Step>

  <Step title="Execute Function">
    Click **Save and Preview** to execute your function
      
    ![Save and Preview](https://api.apidog.com/api/v1/projects/451700/resources/366881/image-preview)
  </Step>

  <Step title="Review Results">
    Review the results in the preview panel:

    - ✔️ Check for successful execution
    - ✔️ Verify the response data
    - ✔️ Look for any errors
    - ✔️ If using a webhook testing service, verify the payload was received
  </Step>
</Steps>

:::check[]
**Congratulations!** You've successfully created your first App Function. 🎉
:::

---

## Accessing Salla APIs

App Functions have built-in access to Salla APIs with automatic authentication:


```typescript Fetch Orders
export default async (context: Order): Promise<Resp> => {
  // Fetch order details from Salla API
  const response = await fetch('https://api.salla.dev/admin/v2/orders', {
    method: 'GET'
    // No need to add Authorization header - it's automatic!
  });

  const orders = await response.json();
  /*
    * The .setData() should be called mandatorily. (Pass {} as default)
    * The .setStatus() is optionallly called. The default status is 200.
    * The .setMessage() is optional. 
    * Incase there is any error invoke Resp.error().
  */
  return Resp.success().setData(orders);
}
```

```typescript Update Product
export default async (context: Order): Promise<Resp> => {
  // Update product via Salla API
  // NOTE: Authentication token is automatically injected by the platform.
  const response = await fetch(`https://api.salla.dev/admin/v2/products/${context.payload.data.id}`, {
    method: 'PUT',
    headers: { 
      'Content-Type': 'application/json' 
      // Authorization header is automatically handled
    },
    body: JSON.stringify({
      name: 'Updated Product Name' // Example: Renaming the product
    })
  });
  
  const product = await response.json();

  // Return the success status of the API call
  return Resp.success().setData(product);
}
```

:::info[]
Explore all available Salla APIs in the [API Reference](https://docs.salla.dev/426392m0)
:::

---

## Best Practices

### 1. Keep Functions Focused

Each function should do one thing well. If you need complex logic, break it into multiple functions or queue the remining impratant jobs

```typescript
// ✅ Good: Focused function
export default async (context: ContextType): Promise<Resp> => {
  await sendEmail(context.payload.data);
  return Resp.success().setData({});
}

// ❌ Bad: Too many responsibilities
export default async (context: ContextType): Promise<Resp> => {
  await sendEmail();
  await updateInventory();
  await syncCRM();
  await generateInvoice();
  // Too much in one function!
}
```

### 2. Return Consistent Responses

Always return `Response` object with `.success` or `.setError`. You can find all the information about handling responses in [🤝 Understanding App Function Responses](https://docs.salla.dev/app-functions/responses.md)

```typescript
// ✅ Success
return Resp.success().setData({ ... });

// ✅ Failure
return Resp.error().setError({ message: "Something went wrong" });
```

### 3. Use Settings for Configuration

Store API keys, URLs, and feature flags in app settings, not in code.

```typescript
// ✅ Good: Use settings
const apiKey = context.settings.externalApiKey;
const webhookUrl = context.settings.webhookUrl;

// ❌ Bad: Hardcoded values
const apiKey = "sk_live_abc123"; // Never do this!
```

### 4. Log Important Information

Use `console.log()` for debugging, but avoid logging sensitive data.

```typescript
// ✅ Good logging
console.log('Processing order:', context.payload.data.id);
console.log('Webhook status:', response.status);

// ❌ Bad logging - Never log sensitive data
console.log('API Key:', settings.apiKey); // Don't!
console.log('Customer data:', customer); // Don't!
```

### 5. Test Thoroughly

Test your functions with various scenarios:

- ✔️ Successful operations
- ✔️ Failed operations
- ✔️ Edge cases (null values, empty arrays)
- ✔️ Different data types

---

## Publishing Your App Functions

After creating and testing your App Functions:

<Steps>
  <Step title="Return to Partner Portal">
    Navigate to the [Salla Partner Portal](https://portal.salla.partners)
  </Step>

  <Step title="Open Your App">
    Go to your app dashboard
  </Step>

  <Step title="Publish Changes">
    Click **Publish** to make your changes live
  </Step>

  <Step title="Notify Merchants">
    Merchants who have installed your app will receive the updates automatically
  </Step>
</Steps>

:::warning[]
**Sandbox vs Production**: Changes are saved in the sandbox environment until you publish. Always test thoroughly before publishing.
:::

---

## Event Types Quick Reference

Understanding when to use synchronous vs asynchronous events:

<Tabs>
  <Tab title="Asynchronous Events">

### Asynchronous Events Lifecycle
      
:::info[]
**When to use**: Notifications, logging, syncing data, analytics, tracking
:::
      
:::info[]
**Works for**: Both merchant actions and customer interactions
:::

```mermaid
sequenceDiagram
    participant Actor as Actor<br/>(Merchant/Customer)
    participant S as Salla Platform
    participant Q as Event Queue
    participant F as Your App Function
    participant E as External Service

    Actor->>S: 1. Completes Action<br/>(e.g., Create Order, View Product)
    S->>Actor: 2. Action Completed<br/>(immediately, < 1s)
    S->>Q: 3. Queue Event
    Note over Q: Event Queued
    Q->>F: 4. Triggers Function<br/>(context with payload + settings)
    Note over F: Executes in Background<br/>(max 30s timeout)
    F->>E: 5. Send Notification/Sync/Track
    E->>F: 6. Response
    Note over F: Return value ignored<br/>(action already complete)
```

**Characteristics:**

- ✔️ Queued instantly (< 1 second) - user never waits
- ✔️ Runs in background after action completes
- ✔️ Doesn't block user experience
- ✔️ Function can take up to 30 seconds to execute
- ✔️ Return value doesn't affect the original action

**Example Events:**


```javascript Merchant Events
order.created
order.updated
order.status.updated
product.created
brand.updated
```

```javascript Customer Events
Product Viewed
Product Added
Cart Updated
Order Completed
```


**Example Use Cases:**


```javascript Merchant Event
export default async (context: Order): Promise<Resp> => {
  const { payload, settings, merchant } = context;

  // Send notification after order is created
  await sendEmail({
    to: payload.data.customer.email,
    subject: `Order ${payload.data.id} Confirmed`,
    body: `Thank you for your order!`
  });
          fetch(`https://api.salla.dev/admin/v2/products/${context.payload.data.id}`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      name: 'Updated Product Name'
    })
  });
      
  // Log to analytics
  await fetch(`https://api.mock.com/analytics`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        orderId: payload.data.id,
        total: payload.data.total
      })
  })
  
  /*
    * The .setData() should be called mandatorily. (Pass {} as default)
    * The .setStatus() is optionallly called. The default status is 200.
    * The .setMessage() is optional. 
    * Incase there is any error invoke Resp.error().
  */
  return Resp.success().setData({}).setMessage('Analytics synced');
}
```

```javascript Customer Event
export default async (context) => Promise<Resp> {
  const { payload, settings, merchant } = context;

  // Track product view
  await analytics.track('Product Viewed', {
    userId: payload.data.userId,
    productId: payload.data.product_id,
    productName: payload.data.name
  });
}
```


  </Tab>

  <Tab title="Synchronous Actions">

### Synchronous Actions (Advanced)

      
:::warning[]
**When to use**: Creation, validation, modification, custom calculations
:::

:::warning[]
**Important**: User is blocked and waiting - must be extremely fast!
:::

**Characteristics:**

- ⚡ Runs immediately before action completes
- ⚡ Blocks the operation until complete
- ⚡ Must complete within 3 seconds
- ⚡ Return value can modify or reject the action

**Example Events:**

```javascript
// Shipment Actions
shipment.creating
```

**Example Use Case:**

```javascript
export default async (context: Shipments): Promise<Shipment> {
  const { payload, settings, merchant } = context;
  const { data: shipment } = payload;

  // Validate shipment address (must be fast - no external API calls!)
  const isValid = await validateAddress(payload.data.address);

  if (!isValid) {
    // Reject the shipment creation
    return Shipment.error()
      .setMessage("Invalid shipping address. Please verify and try again.");
  }

  /// Allow shipment creation to proceed (required: set shipment number)
  return Shipment.success()
    .setShipmentNumber(shipment.id);
}
```

  </Tab>

  <Tab title="Comparison">

### Comparison Table

| Feature | Asynchronous Events | Synchronous Actions |
|---------|---------------------|---------------------|
| **Timing** | After action completes | Before action completes |
| **Blocking** | Non-blocking (queued < 1s) | ⚠️ Blocks operation |
| **Timeout** | 30 seconds (background) | 5-10 seconds |
| **User Impact** | User never waits ✔️ | User waits ⚠️ |
| **Return Impact** | No effect on action | Can modify/reject action |
| **Use Cases** | Notifications, logging, sync, analytics, tracking | Validation, modification |
| **Examples** | `order.created`, `Product Viewed` | `shipment.creating` |

  </Tab>
</Tabs>

---

## Next Steps

Now that you've created your first App Function, explore more advanced features:

- 📋 **[Supported Events](https://docs.salla.dev/app-functions/supported-events.md)** — See all available merchant and customer events
- 🧪 **[Testing App Functions](https://docs.salla.dev/app-functions/testing.md)** — Learn advanced testing techniques
- 📚 **[Salla APIs](https://docs.salla.dev/426392m0)** — Explore Salla API documentation

---

## Need Help?

- 📖 **[Documentation](https://docs.salla.dev)** — Browse complete documentation
- 👤 **[Partner Portal](https://portal.salla.partners)** — Access your developer dashboard
- 👥 **[Community](https://salla.dev/)** — Join our developer community
