Asynchronous operations
How to handle requests that return a notification id, and how to poll for their result.
Some requests cannot finish immediately, because OneBasket has to call a provider system to complete them. Adding a product to a basket is an example. OneBasket has to add the product to the basket in the provider's system and wait for the provider to respond.
These requests return straight away with a notificationId:
{ "notificationId": "..." }You then poll the Notifications API with that id to learn the outcome.
Notification status
GET /notifications/{notificationId} returns a notification:
{
"status": "complete",
"payload": { }
}status | Meaning | What to do |
|---|---|---|
pending | The request is still being processed. | Wait, then poll again. |
complete | The request succeeded. payload holds the result, if the operation has one. | Read the updated resource, for example Get a basket. |
failed | The request did not succeed. payload describes the failure, if details are available. | Show an error to the customer. Read the resource again before you retry. |
Which requests are asynchronous
An endpoint is asynchronous when its response schema in the API reference is NotificationResponse or AcceptedNotificationResponse.
In the Baskets API, every request that changes a basket is asynchronous. Only Create a basket, Get a basket and Delete a basket return the basket directly.
| Endpoint | Response |
|---|---|
| Add a product to a basket | 200 with a notification id |
| Select seats | 200 with a notification id |
| Select time slot | 200 with a notification id |
| Update a line item | 200 with a notification id |
| Update line item quantity | 200 with a notification id |
| Delete line item | 200 with a notification id |
| Set delivery method | 200 with a notification id |
| Assign contact | 200 with a notification id |
| Add voucher and Remove voucher | 200 with a notification id, or 204 when no processing was needed |
| Set customer details | 202 with a notification id, or 204 when no processing was needed |
| Create a payment intent | 200 with the payment intent, or 202 with a notification id |
| Cancel a payment intent | 200 with a notification id |
Check the status code of each response, because some endpoints have both a synchronous and an asynchronous response.
Polling example
type Notification = {
status: 'pending' | 'complete' | 'failed';
payload?: Record<string, unknown>;
};
export async function waitForNotification(
notificationId: string,
{ intervalMs = 500, timeoutMs = 30_000 } = {},
): Promise<Notification> {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const response = await fetch(
`${process.env.ONEBASKET_API_URL}/notifications/${notificationId}`,
{ headers: { 'x-api-key': process.env.ONEBASKET_API_KEY! } },
);
if (!response.ok) throw new Error(`Notification request failed: ${response.status}`);
const notification = (await response.json()) as Notification;
if (notification.status !== 'pending') return notification;
await new Promise((resolve) => setTimeout(resolve, intervalMs));
}
throw new Error(`Notification ${notificationId} was still pending after ${timeoutMs} ms`);
}Recommendations:
- Poll no more often than twice per second.
- Always set a timeout, and show the customer a retry option when it is reached.
- Disable the control that started the request until the notification is no longer
pending. This stops the customer from sending the same change twice.