Salla App Functions place custom code inside the lifecycle of merchant and customer activity without requiring the developer to host that function infrastructure. The current overview describes a context containing event payload, merchant information and per-merchant app settings, with automatic authentication when calling Salla APIs. The important architectural decision, however, is not serverless versus server. It is whether a rule belongs on the user's blocking path or after the business action has already completed.
Salla documents two different contracts. A Synchronous Action blocks the merchant and can modify or reject the operation. The detailed guidance targets under 500 milliseconds, treats under one second as acceptable and sets a five-second maximum that already produces poor experience. An Asynchronous Event is queued without blocking the user, may execute for up to 30 seconds, and its return value cannot change the original action.
That difference is a consistency boundary. Put the wrong work on the synchronous side and every slow dependency becomes checkout or dashboard latency. Put a required validation on the asynchronous side and the system can only react after the invalid operation has succeeded.
Start with the decision, not the event name
Ask one question for each requirement: must this result change whether the current operation is accepted, rejected or modified? If yes, it may belong in a Synchronous Action—but only if the decision can be produced within a strict local latency budget. If the work sends a notification, updates analytics, syncs an ERP, enriches data or starts fulfillment after an accepted action, it belongs on the asynchronous side.
The Supported Events page shows this distinction in practice. Merchant order events such as creation, completion, status update and refund are asynchronous. Customer events are documented as always asynchronous. The shipment model is mixed: `shipment.creating` is synchronous and runs before creation, while shipment-created, cancelled and updated events run in the background.
Do not infer that every event family has a synchronous hook. Build from the supported-event table and each event schema. If the platform exposes only an asynchronous signal, an app cannot safely simulate pre-commit rejection by trying to undo the action afterward. Use compensation only when the business accepts eventual correction and the user experience makes that visible.
Keep the synchronous lane almost boring
A synchronous function should resemble a small decision table: validate required context, read compact settings, compute a deterministic result and return. Avoid a chain of external HTTP calls, heavy database queries, AI inference or a multi-service workflow. Salla's own overview explicitly warns against slow external APIs and complex calculations on this path.
context validation
-> normalized inputs
-> local rule / compact lookup
-> accept, reject or modifyFor a shipping action, the rule might select a preconfigured service level, validate an address field or return an already-known rate. If the rate requires a carrier API, define a hard timeout well below the platform maximum and decide the failure policy before coding. Fail-closed may protect against an invalid shipment but interrupts the merchant; fail-open preserves flow but may defer a problem. That is a product and risk decision, not a generic retry choice.
Budget the full path, not only JavaScript execution: cold start, serialization, network connection, downstream response and response construction all consume the same user-visible time. Track p50, p95 and p99, timeout rate and rejected actions by reason. A five-second platform ceiling is not a performance target.
The Responses documentation makes the consequence explicit: `success: true` proceeds and may apply returned data, while `success: false` halts a synchronous action and exposes the error to the merchant. Keep messages actionable and safe; do not leak internal stack traces, credentials or provider responses.
Treat asynchronous functions as ingress, not a 30-second workflow engine
An asynchronous function does not delay the original operation, but 30 seconds is still a bounded execution window. It is appropriate for a small API update or notification when the operation can finish safely inside the window. A long ERP export, AI enrichment, batch inventory reconciliation or carrier workflow needs durable state outside the function.
Use the App Function as an ingress adapter: validate context, derive a stable operation identity, submit a compact job to your durable queue or inbox, then return. The external worker owns retries, backoff, dead-letter handling, observability and reconciliation. Include only fields required to locate the source entity; fetch current details later when the workflow needs them.
App Function -> durable inbox -> worker -> external system
| |
dedupe retry + receiptSalla's overview says App Functions include built-in retry logic and error handling, but the cited page does not define an exactly-once guarantee or a complete retry schedule. Therefore, design the receiver idempotently. Build a key from documented stable fields available for that event—for example merchant, event type, entity ID and source update value—rather than assuming an undocumented event identifier exists. A duplicate should converge to the same inbox row and external result.
A failed asynchronous response is logged, according to the response guide, but the original store action remains successful. This is why returning `success: false` is not a recovery mechanism for ERP sync. Record the job externally, expose operational status and reconcile from Salla APIs when an event or handoff is missing.
Separate settings, secrets and runtime policy
Each function receives app settings customized per merchant. Treat settings as typed configuration, not trusted arbitrary input. Validate required URLs, modes and identifiers at the boundary. Never write tokens, customer personal data or payment information to console logs; the official testing guide explicitly warns against logging those categories.
Prefer references to secrets where the platform and integration permit it, rotate external credentials, restrict destination hosts where possible and give every merchant an isolated namespace in the durable inbox. A merchant ID in the payload is routing information, not authorization by itself. The called external service must authenticate the function or gateway and enforce which tenant it can mutate.
Keep runtime policy versioned. If a synchronous shipping rule changes, store the rule version with the decision. If an asynchronous job is queued under one mapping and processed after a deployment, the worker should know which schema and transformation produced it. Reject or migrate incompatible payload versions instead of silently guessing.
Test the boundary under failure, not only preview success
Salla documents a Partner Portal preview that runs functions against demo-store data and exposes execution status, response data, duration, console output and errors. Use it to inspect the actual context and confirm required scopes. Then add your own contract fixtures and integration tests because a successful preview does not exercise queue duplication, downstream outage or concurrency.
For synchronous functions, test missing fields, maximum input size, cold execution, slow downstream calls, malformed settings and the exact merchant-facing rejection message. Run a latency test with the external dependency degraded; verify that the function exits under your internal deadline rather than consuming the five-second ceiling.
For asynchronous functions, send the same logical event repeatedly, fail the receiver after it commits but before it responds, delay events out of order and rotate credentials mid-run. Verify that the durable inbox deduplicates, older versions cannot overwrite newer state, unknown outcomes are reconciled and dead-letter work is visible to an operator.
Adopt App Functions with an explicit escape hatch
The current welcome page labels App Functions beta and free, with future pricing based on calls, execution time and resources. Treat that as current documentation, not a permanent commercial promise. Measure function counts and duration now, and keep business-critical workflows portable behind a small domain interface.
App Functions can remove infrastructure from the trigger edge, but they do not remove architecture. The durable design is simple: synchronous functions make only immediate, bounded decisions; asynchronous functions record or hand off eventual work; external workers own long-running reliability. When every requirement has one of those owners, the app remains fast for merchants and recoverable for engineers.
Official references
These references document the tools discussed. Examples and design decisions are illustrative and should be adapted to the project and its versions.
- Salla App Functions — What are App Functions, verified 27 September 2026
- Salla App Functions — Responses, verified 27 September 2026
- Salla App Functions — Supported Events, verified 27 September 2026
- Salla App Functions — Testing, verified 27 September 2026
- Salla App Functions — Shipment Events, verified 27 September 2026
Prepared by: Noor Yasser
Working through a similar engineering challenge?
I help teams turn architecture decisions into a clear scope and dependable, reviewable implementation.




