# Responses

# 🤝 Understanding App Function Responses

Understanding how your App Function returns data is crucial, especially for **Synchronous Actions** where the response directly influences the merchant's operation. This guide details the structure, behavior, and utility tools for handling function responses.

---

## 1. The Core Contract: `Response` 💡

Every App Function is expected to return an object conforming to the `Response` contract. While the structure is required for all functions, its effect on the platform varies significantly based on the execution type.

| Field | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `success` | `boolean` | Yes | Indicates if your function logic completed successfully. |
| `data` | `object` | No | A payload containing data to be returned or applied to the Salla operation (used primarily for **Synchronous Actions**). |
| `error` | `string` | No | A human-readable error message. Required if `success` is `false` for **Synchronous Actions**. |
| `status` | `number` | No | (Optional) An HTTP status code (e.g., 200, 400). Set via the `Response` utility class. |
| `message` | `string` | No | (Optional) An informative message. Set via the `Response` utility class. |

<Info>
**Response Format**: You can return responses in two ways:
- **Plain Object**: Return a simple object with `success`, `data`, and `error` fields
- **Response Utility**: Use the `Response` utility class for more structured responses (recommended for Customer Events and some Merchant Events)
</Info>

---

## 2. Response Behavior by Execution Type 🔄

The platform handles the `Response` differently depending on whether your function is an **Asynchronous Event** or a **Synchronous Action**.

### 2.1 Responses for Synchronous Actions (Blocking)

For synchronous actions (e.g., `shipment.creating`), your response is **critical**. The merchant is blocked and waiting for your function's decision.

<Warning>
**Performance Critical**: Synchronous actions block the user. Your function must respond in **milliseconds** (< 500ms recommended). Keep logic simple and fast!
</Warning>

| Return Value | Platform Action | Example Use Case |
| :--- | :--- | :--- |
| `success: true` | **Proceeds**. The action completes, and any data in the `data` field is merged or applied to the resulting entity. | Validating an address and proceeding with shipment creation. |
| `success: false` | **Rejects**. The action is halted, and the `error` message is displayed to the merchant immediately. | Rejecting a promotion if a custom rule is violated. |

#### Example: Accepting a Synchronous Action

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

  // Quick validation (must be fast!)
  if (shipment.ship_to.country !== 'السعودية') {
    return Shipment.error()
      .setMessage("Shipment creation rejected: The app only supports shipping within Saudi Arabia (SA).");
  }

  // Allow the action to proceed (required: set shipment number)
  return Shipment.success()
    .setShipmentNumber(shipment.id);
    .setStatus(ShipmentStatusEnum.IN_TRANSIT);
}
```

#### Example: Rejecting a Synchronous Action

This immediately stops the operation and tells the merchant why.

```typescript
export default async (context: CustomActionCreatingContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const order = payload.data;

  // Check if the order total is too low for a custom shipping rule
  if (order.amounts.total < 100) {
    return Resp.error()
      .setMessage("Minimum order value of 100 is required for this custom shipping method");
  }

  // If validation passes, proceed
  return  Resp.success().setData({
    validated: true,
    order_id: order.id
  });
}
```

#### Special Case: `shipment.creating` Event

The `shipment.creating` event requires the use of the specialized **`Shipment` utility class** to successfully modify or complete the shipment details (e.g., setting a tracking number, generating a label).

<Note>
**Shipment Utility**: For `shipment.creating` events:
- Context type: `Shipments`
- Return type: `Promise<Shipment>`
- Use the `Shipment` utility class (`Shipment.success()`, `Shipment.error()`) to modify shipment data
- Always call `.setShipmentNumber()` when returning success
- Refer to the Shipment Events documentation for detailed examples.
</Note>

---

### 2.2 Responses for Asynchronous Events (Non-Blocking)

For asynchronous events (e.g., `order.created`, `Product Viewed`), your response is **informational only**. The original action has already completed, and the user is not waiting.

<Info>
**Return Value Ignored**: For asynchronous events, your return value doesn't affect the original action. The action completes immediately, and your function executes in the background.
</Info>

| Return Value | Platform Action | Example Use Case |
| :--- | :--- | :--- |
| `success: true` | **Logged**. The response is logged for debugging purposes, but doesn't affect the operation. | Sending a notification after an order is created. |
| `success: false` | **Logged**. The error is logged, but the original action remains successful. | Failed to sync with external CRM, but order creation succeeded. |

#### Example: Asynchronous Merchant Event

```typescript
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const order = payload.data;

  try {
    // Send notification to external system (non-blocking)
    const response = await fetch(settings.webhookUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${settings.apiKey}`
      },
      body: JSON.stringify({
        event: payload.event,
        merchant_id: merchant.id,
        order_id: order.id,
        total: order.amounts.total,
        timestamp: new Date().toISOString()
      })
    });

    if (!response.ok) {
      // Log error, but don't fail the operation
      console.error(`Webhook failed: ${response.statusText}`);
      return Resp.error()
      .setMessage(response.statusText || 'Unknown error')
      .setStatus(500)
      .setData({ error_type: response.text() });
    }

    // Success response (for logging/debugging)
    return  Resp.success().setData({
      order_id: order.id,
      webhook_status: response.status,
      sent_at: new Date().toISOString()
    });
  } catch (error) {
    // Handle errors gracefully
    console.error('Error sending webhook:', error);
    return Resp.error()
      .setMessage(error.message || 'Unknown error')
      .setStatus(500)
      .setData({ error_type: error.name });
  }
}
```

#### Example: Asynchronous Customer Event

```typescript
export default async (context: ProductViewedEvent): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const product = payload.data;

  try {
    // Track product view in analytics (non-blocking)
    await fetch(settings.analyticsUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${settings.analyticsKey}`
      },
      body: JSON.stringify({
        event: 'product_viewed',
        product_id: product.product_id,
        product_name: product.name,
        user_id: product.userId,
        timestamp: new Date().toISOString()
      })
    });

    // Success response (for logging/debugging)
    return  Resp.success().setData({
      tracked: true,
        product_id: product.product_id
    });
  } catch (error) {
    // Analytics failures shouldn't break the user experience
    console.error('Analytics tracking failed:', error);
    return Resp.error()
      .setMessage(error.message || 'Unknown error')
      .setStatus(500)
      .setData({ error_type: error.name });
  }
}
```

---

## 3. The Response Utility Class 🛠️

The `Response` utility class provides a structured way to create responses. It's particularly useful for Customer Events and some Merchant Events that require more detailed response handling.

### 3.1 Using Resp.success()

Create a successful response with data, optional status code, and optional message.

```typescript
export default async (context: OrderStatusUpdatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const order = payload.data;

  // Process the order status change
  const actionTaken = await processOrderStatus(order);

  const data = {
    order_id: order.id,
    status: order.status.slug,
    action_taken: actionTaken,
    processed_at: new Date().toISOString()
  };

  /*
   * The .setData() should be called mandatorily. (Pass {} as default)
   * The .setStatus() is optionally called. The default status is 200.
   * The .setMessage() is optional.
   * In case there is any error invoke Resp.error().
   */
  return Resp.success()
    .setData(data)
    .setStatus(200)
    .setMessage('Order status processed successfully');
}
```

### 3.2 Using Resp.error()

Create an error response for handling failures.

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

  try {
    // Attempt to send SMS
    const response = await fetch(`${settings.smsApiUrl}/send`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${settings.smsApiKey}`
      },
      body: JSON.stringify({
        to: payload.data.mobile,
        message: payload.data.message
      })
    });

    if (!response.ok) {
      // Return error response
      return Resp.error()
        .setMessage('Failed to send SMS')
        .setStatus(response.status)
        .setData({
          error_code: response.status,
          error_message: await response.text()
        });
    }

    const result = await response.json();

    return Resp.success()
      .setData({
        sms_id: result.id,
        status: result.status,
        to: payload.data.mobile
      })
      .setStatus(200);
  } catch (error) {
    return Resp.error()
      .setMessage(error.message || 'Unknown error occurred')
      .setStatus(500)
      .setData({
        error_type: error.name,
        error_details: error.stack
      });
  }
}
```

### 3.3 Response Utility Methods

| Method | Description | Required | Default |
| :--- | :--- | :--- | :--- |
| `Resp.success()` | Create a successful response | Yes (for success) | - |
| `Resp.error()` | Create an error response | Yes (for errors) | - |
| `.setData(data)` | Set response data object | **Yes** (pass `{}` if no data) | `{}` |
| `.setStatus(code)` | Set HTTP status code | No | `200` |
| `.setMessage(msg)` | Set human-readable message | No | - |

### 3.4 When to Use Response Utility vs Plain Object

| Scenario | Recommended Approach | Reason |
| :--- | :--- | :--- |
| **Customer Events** | `Response` utility | Provides structured responses for tracking/analytics |
| **Synchronous Actions** | Plain object | Simpler, faster (performance critical) |
| **Merchant Events (Simple)** | Plain object | Straightforward, less overhead |
| **Merchant Events (Complex)** | `Response` utility | Better error handling and status codes |
| **Error Handling** | Either (Response utility recommended) | Response utility provides better error structure |

---

## 4. Error Handling Patterns 🚨

Proper error handling ensures your functions fail gracefully and provide useful debugging information.

### 4.1 Basic Error Handling

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

  try {
    // Your logic here
    await processOrder(payload.data);

    return Resp.success()
      .setData({
        processed: true
      })
      .setStatus(200);
  } catch (error) {
    // Log error for debugging
    console.error('Error processing order:', error);
    // Handle errors gracefully
    return Resp.error()
      .setMessage(error.message || 'Unknown error occurred')
      .setStatus(500)
      .setData({
        error_type: error.name,
        error_details: error.stack
    });
  }
}
```

### 4.2 Validation Error Handling

```typescript
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const order = payload.data;

  // Validate required data
  if (!order.id) {
    return Resp.error()
      .setMessage('Order ID is missing')
      .setStatus(500)
      .setData({});
  }

  if (!settings.webhookUrl) {
    return Resp.error()
      .setMessage('Webhook URL not configured in app settings')
      .setStatus(500)
      .setData({});
  }

  // Validate order total
  if (order.amounts.total <= 0) {
    return Resp.error()
      .setMessage('Invalid order total')
      .setStatus(500)
      .setData({});
  }

  // Process if validation passes
  try {
    await sendWebhook(order, settings.webhookUrl);
    return Resp.success()
      .setData({
        order_id: order.id
      })
      .setStatus(200);
  } catch (error) {
    return Resp.error()
      .setMessage(`Failed to send webhook: ${error.message}`)
      .setStatus(500)
      .setData({
        error_type: error.name,
        error_details: error.stack
    });
  }
}
```

### 4.3 External API Error Handling

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

  try {
    const response = await fetch(settings.webhookUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${settings.apiKey}`
      },
      body: JSON.stringify(payload.data),
      // Add timeout for long-running requests
      signal: AbortSignal.timeout(10000) // 10 seconds
    });

    if (!response.ok) {
      return Resp.error()
        .setMessage(`External API error: ${response.status} - ${errorText}`)
        .setStatus(response.status)
        .setData({
          error_code: response.status,
          error_message: await response.text()
        });
    }

    const result = await response.json();
    return Resp.success()
      .setData({
        external_id: result.id,
        status: result.status
      })
      .setStatus(200);
  } catch (error) {
    if (error.name === 'AbortError') {
      return Resp.error()
        .setMessage(error.message || 'Unknown error occurred')
        .setStatus(500)
        .setData({
          error: 'Request timed out after 10 seconds'
      });
    }

    return Resp.error()
      .setMessage(error.message || 'Unknown error occurred')
      .setStatus(500)
      .setData({
        error: `Network error: ${error.message}`
    });
  }
}
```

### 4.4 Error Handling with Response Utility

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

  try {
    // Validate settings
    if (!settings.apiKey) {
      return Resp.error()
        .setMessage('API key not configured')
        .setStatus(400)
        .setData({ missing_setting: 'apiKey' });
    }

    // Process the event
    const result = await processEvent(payload.data);

    return Resp.success()
      .setData(result)
      .setStatus(200)
      .setMessage('Event processed successfully');
  } catch (error) {
    // Handle different error types
    if (error instanceof ValidationError) {
      return Resp.error()
        .setMessage('Validation failed')
        .setStatus(400)
        .setData({ validation_errors: error.errors });
    }

    if (error instanceof NetworkError) {
      return Resp.error()
        .setMessage('Network error occurred')
        .setStatus(503)
        .setData({ retry_after: 60 });
    }

    // Generic error
    return Resp.error()
      .setMessage(error.message || 'Unknown error')
      .setStatus(500)
      .setData({ error_type: error.name });
  }
}
```

---

## 5. Best Practices ✅

### 5.1 Keep Responses Focused

Return only the data that's necessary for the operation or debugging.

```typescript
// ❌ Bad: Returning entire payload
return Resp.success()
    .setData({
        data: context.payload // Too large!
    })
    .setStatus(200)
    .setMessage('Event processed successfully');

// ✅ Good: Returning only relevant data
return Resp.success()
    .setData({
        order_id: context.payload.data.id,
        status: context.payload.data.status,
        processed_at: new Date().toISOString()
    })
    .setStatus(200)
    .setMessage('Event processed successfully');
```

### 5.2 Provide Meaningful Error Messages

Error messages should be clear, actionable, and user-friendly (especially for synchronous actions).

```typescript
// ❌ Bad: Generic error message
return Resp.error()
    .setMessage('Error occurred')
    .setStatus(500)
    .setData({});

// ✅ Good: Specific, actionable error message
return Resp.error()
    .setMessage('Minimum order value of 100 SAR is required for this shipping method. Current order total: 75 SAR.')
    .setStatus(400)
    .setData({});
```

### 5.3 Handle Async vs Sync Differently

Remember that synchronous actions block the user, while async events don't.

```typescript
// ✅ Synchronous Action: Keep it simple and fast
export default async (context: Shipments): Promise<Shipment> => {
  const { payload, settings, merchant } = context;
  const { data: shipment } = payload;
  // Quick validation only - no external API calls!
  if (!isValidAddress(shipment.ship_to)) {
    return Shipment.error()
      .setMessage('Invalid shipping address');
  }
  return Shipment.success()
    .setShipmentNumber(shipment.id);
    .setStatus(ShipmentStatusEnum.IN_TRANSIT);
}

// ✅ Asynchronous Event: Can do more complex operations
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  // External API calls are OK here
  await sendNotification(payload.data);
  await syncWithCRM(payload.data);
  await updateAnalytics(payload.data);
  return Resp.success()
    .setData({})
    .setStatus(200)
    .setMessage('Event processed successfully');
}
```

### 5.4 Always Return a Response

Every function must return a response object, even if the operation fails.

```typescript
// ❌ Bad: Function might not return anything
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  if (someCondition) {
    await doSomething();
    // Missing return statement!
  }
}

// ✅ Good: Always return a response
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  if (someCondition) {
    await doSomething();
    return Resp.success()
      .setData({})
      .setStatus(200)
      .setMessage('Event processed successfully');
  }
  return Resp.success()
    .setData({})
    .setStatus(200)
    .setMessage('Success');
}
```

### 5.5 Log for Debugging, Not for Response

Use `console.log()` for debugging information, not for returning data to the platform.

```typescript
// ✅ Good: Log for debugging
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const order = payload.data;
  console.log('Processing order:', order.id);
  console.log('Order total:', order.amounts.total);

  // Return structured response
  return Resp.success()
    .setData({
      order_id: order.id,
      processed: true
    })
    .setStatus(200)
    .setMessage('Event processed successfully');
}

// ❌ Bad: Don't rely on logs for response data
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  console.log('Order data:', payload.data); // Not returned to platform!
  // Missing return statement
}
```

### 5.6 Validate Before Processing

Always validate required data and settings before processing.

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

  // Validate payload
  if (!payload.data?.id) {
    return Resp.error()
      .setMessage('Order ID is missing from payload')
      .setStatus(400)
      .setData({});
  }

  // Validate settings
  if (!settings.webhookUrl) {
    return Resp.error()
      .setMessage('Webhook URL not configured. Please configure it in app settings.')
      .setStatus(400)
      .setData({});
  }

  // Process if validation passes
  try {
    await sendWebhook(payload.data, settings.webhookUrl);
    return Resp.success()
      .setData({})
      .setStatus(200)
      .setMessage('Event processed successfully');
  } catch (error) {
    return Resp.error()
      .setMessage(`Webhook failed: ${error.message}`)
      .setStatus(400)
      .setData({});
  }
}
```

---

## 6. Common Patterns and Examples 📚

### 6.1 Webhook Notification Pattern

```typescript
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const order = payload.data;

  try {
    const response = await fetch(settings.webhookUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${settings.apiKey}`
      },
      body: JSON.stringify({
        event: payload.event,
        merchant_id: merchant.id,
        order_id: order.id,
        reference_id: order.reference_id,
        total: order.amounts.total,
        currency: order.currency,
        customer_email: order.customer.email,
        timestamp: new Date().toISOString()
      })
    });

    if (!response.ok) {
      return Resp.error()
        .setMessage(`Webhook failed: ${response.status} ${response.statusText}`)
        .setStatus(500)
        .setData({});
    }

    return Resp.success()
      .setData({
        order_id: order.id,
        webhook_status: response.status
      })
      .setStatus(200)
      .setMessage('Event processed successfully');
  } catch (error) {
    return Resp.error()
      .setMessage(error.message || 'Unknown error occurred')
      .setStatus(500)
      .setData({
        error_type: error.name,
        error_details: error.stack
    });
  }
}
```

### 6.2 Analytics Tracking Pattern

```typescript
export default async (context: ProductViewedEvent): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const product = payload.data;

  const analyticsData = {
    event: 'product_viewed',
    product_id: product.product_id,
    product_name: product.name,
    product_price: product.price,
    user_id: product.userId,
    timestamp: new Date().toISOString()
  };

  try {
    await fetch(settings.analyticsUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${settings.analyticsKey}`
      },
      body: JSON.stringify(analyticsData)
    });

    return Resp.success()
      .setData({
        tracked: true,
        product_id: product.product_id
      })
      .setStatus(200);
  } catch (error) {
    // Analytics failures shouldn't break user experience
    console.error('Analytics tracking failed:', error);
    return Resp.error()
      .setMessage('Analytics tracking failed')
      .setStatus(500)
      .setData({ error: error.message });
  }
}
```

### 6.3 Data Validation Pattern

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

  // Validate shipping address
  if (!shipment.ship_to) {
    return Shipment.error()
      .setMessage('Shipping address is required');
  }

  // Validate country
  if (shipment.ship_to.country !== 'السعودية') {
    return Shipment.error()
      .setMessage('This shipping method only supports deliveries within Saudi Arabia');
  }

  // Validate order total (if applicable)
  if (shipment.total?.amount < settings.minimumOrderValue) {
    return Shipment.error()
      .setMessage(`Minimum order value of ${settings.minimumOrderValue} SAR is required`);
  }

  // All validations passed (required: set shipment number)
  return Shipment.success()
    .setShipmentNumber(shipment.id);
    .setStatus(ShipmentStatusEnum.IN_TRANSIT);
}
```

### 6.4 External API Integration Pattern

```typescript
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  const order = payload.data;

  try {
    // Call external API with timeout
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 10000); // 10s timeout

    const response = await fetch(settings.externalApiUrl, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${settings.externalApiKey}`
      },
      body: JSON.stringify({
        order_id: order.id,
        total: order.amounts.total,
        items: order.items
      }),
      signal: controller.signal
    });

    clearTimeout(timeout);

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({}));
      return Resp.error()
        .setMessage(`External API error: ${response.status} - ${errorData.message || response.statusText}`)
        .setStatus(500)
        .setData({});
    }

    const result = await response.json();

    return Resp.success()
      .setData({
        order_id: order.id,
        external_id: result.id,
        sync_status: 'success'
      })
      .setStatus(200);
  } catch (error) {
    if (error.name === 'AbortError') {
      return Resp.error()
        .setMessage('Request timed out. Please try again.')
        .setStatus(500)
        .setData({});
    }

    return Resp.error()
        .setMessage(`Integration error: ${error.message}`)
        .setStatus(500)
        .setData({});
  }
}
```

---

## 7. Quick Reference 🎯

### 7.1 Response Format Comparison

| Aspect | Plain Object | Response Utility |
| :--- | :--- | :--- |
| **Syntax** | Simple object literal | Method chaining |
| **Use Case** | Synchronous actions, simple events | Customer events, complex error handling |
| **Performance** | Faster (less overhead) | Slightly more overhead |
| **Error Handling** | Basic | Advanced (status codes, messages) |
| **Type Safety** | Good | Excellent |

### 7.2 Response Fields Quick Reference

| Field | When to Use | Example |
| :--- | :--- | :--- |
| `success: true` | Operation completed successfully | `{ success: true, data: {...} }` |
| `success: false` | Operation failed | `{ success: false, error: "..." }` |
| `data` | Return relevant data | `{ success: true, data: { order_id: 123 } }` |
| `error` | Provide error message | `{ success: false, error: "Validation failed" }` |
| `status` | Set HTTP status code (Response utility) | `Resp.success().setStatus(201)` |

### 7.3 Execution Type Quick Reference

| Type | Response Impact | Performance | Example |
| :--- | :--- | :--- | :--- |
| **Synchronous Action** | **Critical** - Affects operation | Must be < 500ms | `shipment.creating` |
| **Asynchronous Event** | **Informational** - Logged only | Can take up to 30s | `order.created`, `Product Viewed` |

---

## 8. Troubleshooting 🔍

### Issue: Response Not Affecting Operation

**Symptom**: Your function returns `success: false`, but the operation still completes.

**Solution**: Check if you're using an asynchronous event. Async events don't affect the original operation - they execute in the background after the action completes.

```typescript
// For async events, the return value is for logging only
export default async (context: OrderCreatedContext): Promise<Resp> => {
  const { payload, settings, merchant } = context;
  // This won't stop order creation - it's already done!
  return Resp.error()
        .setMessage('This error is logged but doesn\'t affect the order')
        .setStatus(500)
        .setData({});
}
```

### Issue: Synchronous Action Too Slow

**Symptom**: Merchant experiences delays when performing actions.

**Solution**: Remove slow operations (external API calls, complex calculations) from synchronous actions. Keep them simple and fast.

```typescript

// ❌ Bad: Slow external API call in synchronous action
export default async (context: Shipments): Promise<Shipment> => {
  const { payload, settings, merchant } = context;
  await fetch('https://slow-api.com/validate'); // Too slow!
  return Shipment.success()
    .setShipmentNumber(payload.data.id);
    .setStatus(ShipmentStatusEnum.IN_TRANSIT);
}

// ✅ Good: Quick validation only
export default async (context: Shipments): Promise<Shipment> => {
  const { payload, settings, merchant } = context;
  const { data: shipment } = payload;
  // Fast local validation
  if (!isValid(shipment)) {
    return Shipment.error()
      .setMessage('Validation failed');
  }
  return Shipment.success()
    .setShipmentNumber(shipment.id);
    .setStatus(ShipmentStatusEnum.IN_TRANSIT);
}
```

### Issue: Error Message Not Displayed

**Symptom**: Error message in response doesn't show to merchant.

**Solution**: For synchronous actions, ensure `error` field is a string and `success` is `false`. For async events, errors are logged but not displayed to users.

```typescript
// ✅ Correct format for synchronous actions
return Shipment.error()
    .setMessage(
      'Clear, user-friendly error message' // Must be a string
    );

// ❌ Wrong format
return Shipment.error()
    .setMessage(
      'Error' // Should be string, not object
    );
```

---

## Summary 📝

- **Synchronous Actions**: Response is critical - affects operation, must be fast (< 500ms)
- **Asynchronous Events**: Response is informational - logged only, doesn't affect operation
- **Plain Object**: Simple, fast, good for sync actions
- **Response Utility**: Structured, good for customer events and complex error handling
- **Error Handling**: Always provide clear, actionable error messages
- **Best Practices**: Keep responses focused, validate data, handle errors gracefully

---

## Next Steps 🚀

Now that you understand App responses:

- 📋 **[Testing App Functions](https://docs.salla.dev/app-functions/testing.md)** — Learn how to test your functions and verify responses
- 🚀 **[Get Started](https://docs.salla.dev/app-functions/get-started.md)** — Create your first App Function
- 📚 **[Event Reference](https://docs.salla.dev/app-functions/supported-events.md)** — Explore all available events and their response requirements
- 🛠️ **[Salla APIs](https://docs.salla.dev/426392m0)** — Access Salla APIs from your functions
