# Create a session Source: https://docs.tabby.sa/api-reference/checkout/create-a-session post /api/v2/checkout Creates a Checkout session. Creates Session and Payment, returns Pre-Scoring result (status), ids of Payment and Session. # Session creation payload model Source: https://docs.tabby.sa/api-reference/checkout/session-payload-model ## Request The payload example for a session creation request with all required fields for a direct API custom integration. Fields marked `// required` are mandatory for the full checkout session — the [background pre-scoring call](/pay-in-4-custom-integration/checkout-flow#background-pre-scoring-check) accepts a minimal subset. Fields marked `// recommended` are optional, but improve scoring accuracy and approval rates — send them whenever you have the data. The `//` comments are annotations, not part of the payload — strip them before sending. ```jsonc theme={"dark"} { "payment": { // required "amount": "100", // required. Up to 2 decimals for UAE and KSA, e.g. 100.00 "currency": "AED", // required. Use the ISO 4217 standard for defining currencies "description": "test payload", "buyer": { // required "name": "John Doe", // required. Customer's full name "email": "jsmith@example.com", //required. Customer's email address "phone": "500000001", //required. Customer's phone number "dob": "2000-01-20" }, "shipping_address": { // required "city": "Dubai", // required. Name of city, municipality, or village "address": "Dubai", // required. Building name, apartment number "zip": "1111" // required. Postal code }, "order": { // required "reference_id": "1001", // required. Merchant-assigned order number. "updated_at": "2023-11-07T05:31:56Z", "tax_amount": "0.00", "shipping_amount": "0.00", "discount_amount": "0.00", "items": [ // required { "reference_id": "SKU123", "title": "Name of the product", // required. Name of the product. "description": "Description of the product", "quantity": 1, // required. Quantity of the product ordered. Should be >= 1 "unit_price": "0.00", // required. Price per unit of the product. Should be positive or zero. "discount_amount": "0.00", "image_url": "https://example.com/", "product_url": "https://example.com/", "gender": "Kids", "category": "Clothes", // required. Name of high-level category (Clothes, Electronics,etc.) "color": "white", "product_material": "cotton", "size_type": "EU", "size": "M", "brand": "Name of the Brand", "is_refundable": true, "barcode": "12345678", "ppn": "MNXT2ZM/A", "seller": "Name of the Seller" } ] }, "buyer_history": { // required "registered_since": "2023-11-07T05:31:56Z", // required. Date and time the customer got registred with you "loyalty_level": 0, // recommended. Customer's loyalty level within your store "wishlist_count": 0, "is_social_networks_connected": true, "is_phone_number_verified": true, "is_email_verified": true }, "order_history": [ // recommended. 5-10 previous orders { "purchased_at": "2023-11-07T05:31:56Z", // recommended. Date and time the order was placed "amount": "100", // recommended. Up to 2 decimals for UAE and KSA, e.g. 100.00 "payment_method": "card", "status": "new", // recommended. Status of the order "buyer": { // recommended "name": "John Doe", // recommended. Customer's full name "email": "jsmith@example.com", // recommended. Customer's email address "phone": "500000001", // recommended. Customer's phone number "dob": "2000-01-20" }, "shipping_address": { // recommended "city": "Dubai", // recommended. Name of city, municipality, or village "address": "Dubai", // recommended. Building name, apartment number "zip": "1111" // recommended. Postal code }, "items": [ { "reference_id": "SKU123", "title": "Name of the product", "description": "Description of the product", "quantity": 1, "unit_price": "0.00", "discount_amount": "0.00", "image_url": "https://example.com/", "product_url": "https://example.com/", "gender": "Kids", "category": "Clothes", "color": "white", "product_material": "cotton", "size_type": "EU", "size": "M", "brand": "Name of the Brand", "is_refundable": true, "barcode": "12345678", "ppn": "MNXT2ZM/A", "seller": "Name of the Seller" } ] } ], "meta": { "customer": "#customer-id", "order_id": "#1234" }, "attachment": { "body": "{\"flight_reservation_details\": {\"pnr\": \"TR9088999\",\"itinerary\": [...],\"insurance\": [...],\"passengers\": [...],\"affiliate_name\": \"some affiliate\"}}", "content_type": "application/vnd.tabby.v1+json" } }, "lang": "en", // required. Session language "merchant_code": "code provided to you from Tabby side", // required. Merchant's branch code "merchant_urls": { "success": "https://your-store/success", "cancel": "https://your-store/cancel", "failure": "https://your-store/failure" }, "token": null } ``` ## Response A successful session creation returns `200 OK` with this structure (the `payment` object echoes your request payload back, trimmed here for brevity): ```jsonc theme={"dark"} { "id": "e5f9d6c8-...", // session id "status": "created", // "created" | "rejected" | "expired" "payment": { "id": "01a04380-64b8-8432-a20f-904a48584390", // payment id — SAVE IT: used to verify, capture and refund "created_at": "2026-01-20T09:12:00Z", "status": "CREATED", "is_test": true, "amount": "100.00", "currency": "AED" // ...the rest of your request payload echoed back }, "configuration": { "available_products": { "installments": [ { "web_url": "https://checkout.tabby.ai/?sessionId=...&apiKey=pk_test_...&product=installments", "qr_code": "https://api.tabby.ai/api/v2/checkout/.../hpp_link_qr?product_type=installments" } ] }, "products": { "installments": { "type": "installments", "is_available": true, "rejection_reason": null } } }, "merchant_urls": { "success": "https://your-store/success", "cancel": "https://your-store/cancel", "failure": "https://your-store/failure" }, "merchant": { "name": "Your store name" } } ``` What to read from it: * **`status`** — `created` means the customer can pay; `rejected` means hide Tabby or show the [rejection message](/pay-in-4-custom-integration/checkout-flow#possible-rejection_reason-values). * **`payment.id`** — save it; all later calls ([verify](/api-reference/payments/retrieve-a-payment), capture, refund) address the payment by this id. * **`configuration.available_products.installments[0].web_url`** — the Hosted Payment Page link to redirect the customer to. `installments` is an **array**: take the first element. Always validate `web_url` is present before redirecting. * On rejection (`status: "rejected"`), the reason is in `configuration.products.installments.rejection_reason` (`null` in `created` responses), and `configuration.available_products` comes back **empty** — no `web_url` to redirect to. # Register a dispute webhook Source: https://docs.tabby.sa/api-reference/dispute-webhooks/register-a-dispute-webhook post /api/v1/dispute-webhooks Registers a dispute webhook for the merchant identified by the secret key and the `X-Merchant-Code` header. Disputes have no test mode: authorize with a production secret key (`sk_...`) — a request signed with a test key (`sk_test_...`) is rejected with `403`. Each `merchant_code` can have up to 4 dispute webhooks; the limit is separate from payment webhooks. The `url` is normalised before it is stored (lower-case scheme and host, default port and trailing slash removed) and must be unique per merchant. `header.value` is a secret: it is accepted in full and returned masked in every response, including this one — `****` followed by the last 4 characters for values longer than 8 characters, `****` alone for shorter values. > [!NOTE] > > This is a public, self-service endpoint. Call `https://api.tabby.ai/api/v1/dispute-webhooks` (`https://api.tabby.sa/api/v1/dispute-webhooks` for KSA) with your **live** secret key and the `X-Merchant-Code` header. How dispute webhooks work, payload and statuses: [Dispute webhooks](/pay-in-4-custom-integration/dispute-webhooks). # Remove a dispute webhook Source: https://docs.tabby.sa/api-reference/dispute-webhooks/remove-a-dispute-webhook delete /api/v1/dispute-webhooks/{id} Removes the specified dispute webhook. # Retrieve a dispute webhook Source: https://docs.tabby.sa/api-reference/dispute-webhooks/retrieve-a-dispute-webhook get /api/v1/dispute-webhooks/{id} Retrieves the specified dispute webhook. `header.value` is returned masked. # Retrieve all dispute webhooks Source: https://docs.tabby.sa/api-reference/dispute-webhooks/retrieve-all-dispute-webhooks get /api/v1/dispute-webhooks Retrieves all dispute webhooks registered for the merchant. Returns an empty array when none are registered. # Update a dispute webhook Source: https://docs.tabby.sa/api-reference/dispute-webhooks/update-a-dispute-webhook put /api/v1/dispute-webhooks/{id} Updates the specified dispute webhook. The request **replaces the whole object**: send both `url` and `header`; omitting `header` removes it. The same URL rules as on registration apply (normalised, unique per merchant). # Approve disputes Source: https://docs.tabby.sa/api-reference/disputes/approve-disputes post /api/v1/disputes/approve Approve disputes (refund money to the customer). Only 20 disputes can be approved within a single request. # Challenge disputes Source: https://docs.tabby.sa/api-reference/disputes/challenge-disputes post /api/v1/disputes/challenge Challenge disputes (request Tabby support to take a look at the case). Only 20 disputes can be challenged within a single request. Only disputes with status 'new' might be challenged. # Get dispute by id Source: https://docs.tabby.sa/api-reference/disputes/get-dispute-by-id get /api/v1/disputes/{disputeId} Returns detailed information about dispute. # Get disputes list Source: https://docs.tabby.sa/api-reference/disputes/get-disputes-list get /api/v1/disputes Returns list of 100 recently created disputes. # Provide evidence for a dispute Source: https://docs.tabby.sa/api-reference/disputes/provide-evidence post /api/v1/disputes/{disputeId}/provide-evidence Provide evidence (text and/or attachments) for a dispute. Used by the merchant to submit proof in response to an evidence request. # Upload attachment Source: https://docs.tabby.sa/api-reference/disputes/upload-attachment post /api/v1/disputes/attachments/upload Upload an attachment. Attachment must be in PNG, JPEG or PDF format and can be up to 5 megabytes in size. Files larger than 5 MB are rejected. # API Reference Documentation Source: https://docs.tabby.sa/api-reference/overview In this part of the documentation all the needed API calls for Tabby Integration can be found and tested: * Checkout - it is a whole process of customer data collection and payment authorization. * Payments - the core of Tabby is a payments flow enabling you to handle payments at your webstore. * Webhooks - serve to ensure a seamless payment verification flow. * Dispute webhooks - notify you when a dispute is opened on your payment or changes its status. * Disputes - help merchants resolve issues with customers orders more efficiently. ## Base URLs Tabby uses region-specific domains. Choose the base URL based on the merchant's operating country: | Region | API Base URL | Checkout / Promo | Merchant Dashboard | | - | - | - | - | | **UAE** | `https://api.tabby.ai` | `checkout.tabby.ai` | `merchant.tabby.ai` | | **KSA** | `https://api.tabby.sa` | `checkout.tabby.sa` | `merchant.tabby.sa` | All API paths and payloads are identical across both domains. Tabby identifies the environment (test or live) based on the API keys used. > [!WARNING] > > **Use the base URL that matches the merchant's region for every call.** Many existing integrations call `api.tabby.ai` for all regions and rely on Tabby-side request routing — but not every endpoint is routed (e.g. payments retrieval and disputes), and cross-region calls can return `404`/`403`. New integrations must pin all calls — session creation, payment retrieval, captures, refunds, webhooks, dispute webhooks, disputes — to the regional base URL. | Contact | UAE | KSA | | - | - | - | | **Partner Support** | `partner@tabby.ai` | `partner@tabby.sa` | | **Customer Support** | `help@tabby.ai` | `help@tabby.sa` | ## OpenAPI Specification To access and download the raw OpenAPI specification file kindly use this: [openapi.yaml](https://docs.tabby.ai/openapi.yaml). You can also view this API specification using [ReDoc](https://redocly.github.io/redoc/?url=https://docs.tabby.ai/openapi.yaml), the same is present below: [Tabby API Swagger UI](https://redocly.github.io/redoc/?url=https://docs.tabby.ai/openapi.yaml) ## Quick Links to Documentation * [Direct API Integration Guide](/pay-in-4-custom-integration/quick-start) * [Testing Credentials](/testing-guidelines/testing-credentials) * [Postman API Collection](/testing-guidelines/postman-api-collections) # Capture a payment Source: https://docs.tabby.sa/api-reference/payments/capture-a-payment post /api/v2/payments/{id}/captures Send a Capture requests for Authorized payments only. If you capture the full payment amount, the payment will be automatically closed with full capture. If you capture partial amount, the payment will remain Authorized until the rest of the amount is captured or Close request sent. # Close a payment Source: https://docs.tabby.sa/api-reference/payments/close-a-payment post /api/v2/payments/{id}/close Closed is the final status of the payment. Your payment is going to be closed automatically if you capture the full amount of the payment. If an order is fully cancelled, please close the payment without capturing it - the customer will be refunded for all paid amount. If only a part of the order is delivered, please capture this part and close the payment – it will mean that another part of the order is not going to be delivered to the customer. # List of all payments Source: https://docs.tabby.sa/api-reference/payments/list-of-all-payments get /api/v2/payments Returns a list of success payments (`AUTHORIZED` or `CLOSED`) you’ve previously created. The payments are returned in sorted order, with the most recent payments appearing first. Each entry in the array is a separate payments object, including the status, any captures and any refunds. If no more payments are available, the resulting array will be empty. Payments might be filtered by creation date. # Refund a payment Source: https://docs.tabby.sa/api-reference/payments/refund-a-payment post /api/v2/payments/{id}/refunds Send a full or partial refund amount request. You can only refund the payment that has Closed status. By default refunds reflect instantly, which means Tabby initiates a refund through payment gateway. # Retrieve a payment Source: https://docs.tabby.sa/api-reference/payments/retrieve-a-payment get /api/v2/payments/{id} Retrieves the specified payment. Returns the entire payment object, including the payment Status, Captures and Refunds objects. # Update a payment Source: https://docs.tabby.sa/api-reference/payments/update-a-payment put /api/v2/payments/{id} Updates the reference_id. You can only use this endpoint to update this 1 field. If you send other fields in the request, they will simply be ignored by tabby. The payment to be updated can have a status of AUTHORIZED or CLOSED. # API Error Codes Source: https://docs.tabby.sa/api-reference/response-codes Errors returned by the Tabby API — status codes, response bodies, and how to handle each one. Check the usual errors the Tabby API returns and what to do about each. ## How errors work When a request fails, Tabby responds with a `4xx`/`5xx` HTTP status code and a JSON error body: ```json theme={"dark"} { "status": "error", "errorType": "bad_data", "error": "could not decode request" } ``` For field-level validation failures the body carries an `errors` array instead of a single `error` string: ```json theme={"dark"} { "status": "error", "errorType": "bad_data", "errors": [ { "field": "$.payment.currency", "code": "invalid_value", "message": "" } ] } ``` * Branch your handling on the **HTTP status code** and **`errorType`**. Possible `errorType` values: `bad_data`, `not_authorized`, `no_permission`, `not_found`, `conflict`; a `500` carries `internal`. * `error` is a human-readable English message to aid debugging — log it, but don't parse it and never show it to customers. * On `500` or a network failure, retry per your retry policy; for [captures](#post-apiv2paymentsidcaptures) and [refunds](#post-apiv2paymentsidrefunds) always retry with the **same idempotency key** — your own unique `reference_id` in the request body, see [Idempotent requests](/pay-in-4-custom-integration/payment-processing#idempotent-requests) — so retries can't duplicate money movement. * Use the base URL that matches the merchant's region: `api.tabby.ai` (UAE) or `api.tabby.sa` (KSA) — see [Base URLs](/api-reference/overview#base-urls). Calling the wrong region returns `403` (see below). * A pre-scoring **rejection is not an error**: `POST /api/v2/checkout` returns `200 OK` with `status: "rejected"`. For handling and the customer-facing rejection messages, see [Show rejection](/pay-in-4-custom-integration/checkout-flow#possible-rejection_reason-values). ## Quick reference | HTTP status | Error code (`errorType`) | | - | - | | 400 Bad Request | `bad_data` | | 401 Unauthorized | `not_authorized` | | 403 Forbidden | `no_permission` | | 404 Not Found | `not_found` | | 409 Conflict | `conflict` | | 429 Too Many Requests | — (rate limit reached, see [Rate limit](/introduction/technical-requirements#rate-limit)) | | 500 Internal Server Error | `internal` (Tabby-side error — retry, monitor the [status page](https://www.tabby-status.com/)) | *** ## `POST /api/v2/checkout` Creates a checkout session. Authenticate with the **Secret Key**: `Authorization: Bearer {secret_key}`. > **400 Bad Request — could not decode request** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "could not decode request" } > ``` > > The JSON is malformed or a parameter has the wrong type. Validate the payload against the [Session payload model](/api-reference/checkout/session-payload-model). > **400 Bad Request — currency / merchant\_code mismatch** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "could not create payment: only '{{}}' is supported, but got '{{}}'" } > ``` > > The `currency` in the payload does not match the country assigned to your `merchant_code` (e.g. a UAE `merchant_code` used with `SAR`). Use the correct currency for the country. > **400 Bad Request — field validation failed** > > ```json theme={"dark"} > { > "status": "error", > "errorType": "bad_data", > "errors": [ > { "field": "$.payment.currency", "code": "invalid_value", "message": "" } > ] > } > ``` > > A field value is invalid — `field` points at the offending path. The same envelope covers unsupported `currency`, wrong formats, and similar per-field issues. > **400 Bad Request — payment validation failed** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "could not create payment: should be positive" } > ``` > > Other validation failures on the payment object (amounts and quantities must be positive, etc.) use the same envelope with a human-readable `error`. Read the `error` field for the specific cause. > **401 Unauthorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_authorized", "error": "merchant is null" } > ``` > > Two possible causes: > > 1. The `Authorization` header is missing or wrong — it must be sent as `Authorization: Bearer {secret_key}`. > 2. The `merchant_code` in the payload doesn't match what the API key is bound to. Check the `merchant_code` you're passing and use the correct one for the environment (live vs sandbox). > **403 Forbidden — no permission** > > ```json theme={"dark"} > { "status": "error", "errorType": "no_permission" } > ``` > > Two possible causes: > > 1. You are passing the `token` parameter, but it is not enabled for your integration. Remove `token` from the payload. > 2. The request hit the wrong regional base URL — the body then also carries `"error": "merchant region does not match cluster region"`. Use the base URL that matches the merchant's region: `api.tabby.ai` (UAE) or `api.tabby.sa` (KSA). > **404 Not Found** > > ``` > 404 page not found > ``` > > The URL path is incorrect. Verify it against the [API reference](/api-reference/checkout/create-a-session). Unsupported HTTP methods also return `404`. > **500 Internal Server Error** > > ```json theme={"dark"} > { "status": "error", "errorType": "internal", "error": "Internal Server Error" } > ``` > > Retry the request per your retry policy. If all requests keep returning `500`, contact the Tabby Integrations team and consider hiding Tabby temporarily. Follow [tabby-status.com](https://www.tabby-status.com/) for maintenance and incident updates. *** ## `GET /api/v2/payments/{id}` Retrieves the current state of a payment. Successful responses and payment statuses are covered in [Payment statuses](/pay-in-4-custom-integration/payment-statuses). > **400 Bad Request — invalid payment id** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "invalid payment id" } > ``` > > The `payment_id` is not a valid UUID. Fix the format. > **401 Unauthorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_authorized" } > ``` > > The `Authorization` header is missing or wrong. It must be sent as `Authorization: Bearer {secret_key}`. > **404 Not Found — no such payment** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_found", "error": "no such payment" } > ``` > > No payment with this `payment_id` is visible to your API key — either the id is wrong, or the payment belongs to a different store / API key. Check the `payment_id` and that you query with the same key that created the session. > **500 Internal Server Error** > > ```json theme={"dark"} > { "status": "error", "errorType": "internal", "error": "Internal Server Error" } > ``` > > Retry per your retry policy. If `500` persists, contact the Tabby Integrations team and check [tabby-status.com](https://www.tabby-status.com/). *** ## `GET /api/v2/payments` Retrieves a list of payments filtered by query parameters. Errors use the same envelopes as `GET /api/v2/payments/{id}`: * `400 Bad Request` (`bad_data`) — an invalid query-parameter value: `"error": "failed to decode request"`. * `401 Unauthorized` (`not_authorized`) — missing/wrong `Authorization` header. * `500 Internal Server Error` (`internal`) — retry per your retry policy. *** ## `PUT /api/v2/payments/{id}` Updates a payment (e.g. `reference_id`). Errors use the same envelopes as the other Payments endpoints: * `400 Bad Request` (`bad_data`) — malformed body: `"error": "failed to decode request"`. * `401 Unauthorized` (`not_authorized`) — missing/wrong `Authorization` header. * `404 Not Found` (`not_found`) — `"error": "no such payment"`: wrong `payment_id`, or a payment of a different store / API key. * `500 Internal Server Error` (`internal`) — retry per your retry policy. *** ## `POST /api/v2/payments/{id}/captures` Captures funds from an `AUTHORIZED` payment. > [!TIP] > > Always send an idempotency key — your own unique `reference_id` in the request body (see [Idempotent requests](/pay-in-4-custom-integration/payment-processing#idempotent-requests)) — so that retries after a `500` or `409` do not produce duplicate captures. > **400 Bad Request — invalid amount or body** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "Errors: [invalid_value($.amount):{{}}" } > ``` > > The `amount` has a wrong value — negative, too many decimal places, etc. An empty or truncated request body returns the same envelope with `"error": "EOF"`. > **400 Bad Request — amount exceeds remaining** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "could not capture: could not capture more than not captured yet: {{}}" } > ``` > > The amount is higher than the remaining uncaptured amount. Examples: > > 1. Total 100, capture request 150. > 2. Total 100, one successful capture of 50, another capture for 100. > 3. Total 100, one successful capture of 100, another capture for 100. > > Case 3 is usually an unsafe retry — use an idempotency key instead of re-sending the capture. > **400 Bad Request — payment not authorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "could not capture not authorized payments" } > ``` > > The payment status is not `AUTHORIZED`, so captures cannot be made. Check the payment status via `GET /api/v2/payments/{id}` and act accordingly. > **400 Bad Request — already closed** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "already closed" } > ``` > > The payment is already in a terminal `CLOSED` state — usually a duplicate capture of an already fully-captured payment, or a capture after `close`. Check the payment status; if your first capture actually succeeded, no action is needed. > **400 Bad Request — invalid payment id** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "invalid payment id" } > ``` > > The `payment_id` is not a valid UUID. Fix the format. > **401 Unauthorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_authorized" } > ``` > > The `Authorization` header is missing or wrong. It must be sent as `Authorization: Bearer {secret_key}`. > **404 Not Found — no such payment** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_found", "error": "no such payment" } > ``` > > No payment with this `payment_id` is visible to your API key — wrong id, or a payment of a different store / API key. > **409 Conflict — concurrent request** > > ```json theme={"dark"} > { "status": "error", "errorType": "conflict", "error": "could not acquire lock: payment is locked" } > ``` > > Another concurrent request for this payment is being processed. Retry shortly with the same idempotency key. > **500 Internal Server Error** > > ```json theme={"dark"} > { "status": "error", "errorType": "internal", "error": "Internal Server Error" } > ``` > > If you send an idempotency key, retry safely per your retry policy. Without one, call `GET /api/v2/payments/{id}` first and check already-applied captures before retrying. If `500` persists, contact the Tabby Integrations team and check [tabby-status.com](https://www.tabby-status.com/). *** ## `POST /api/v2/payments/{id}/refunds` Refunds a captured (`CLOSED`) payment. > [!TIP] > > Always send an idempotency key — your own unique `reference_id` in the request body (see [Idempotent requests](/pay-in-4-custom-integration/payment-processing#idempotent-requests)) — to make refund retries safe. > **400 Bad Request — invalid amount format** > > ```json theme={"dark"} > { > "status": "error", > "errorType": "bad_data", > "errors": [ > { "field": "$.amount", "code": "invalid_value", "message": "money supports only 3 decimal places, but got: 5" } > ] > } > ``` > > The `amount` has a wrong value — too many decimal places for the currency, zero or negative (`"error": "refund amount is less then or equals zero"`), or not a number at all (`"error": "error decoding string 'NaN': can't convert NaN to decimal"`). > **400 Bad Request — amount exceeds refundable** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "refund amount should be less or equal than billed amount" } > ``` > > The refund amount exceeds the still-refundable amount. Examples: > > 1. Captured 100, refund request 150. > 2. Captured 100, one successful refund of 50, another refund for 100. > 3. Captured 100, one successful refund of 100, another refund for 100. > > Case 3 is usually an unsafe retry — use an idempotency key instead of re-sending the refund. > **400 Bad Request — cannot refund payment** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "can not refund payment that has no captures and not closed" } > ``` > > The payment is not `CLOSED` or has no captures. Check the payment status and act accordingly. > **400 Bad Request — already closed** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "already closed" } > ``` > > The payment is in a state that no longer accepts this refund (e.g. it was closed without captures). Check the payment status via `GET /api/v2/payments/{id}`. > **401 Unauthorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_authorized" } > ``` > > The `Authorization` header is missing or wrong. It must be sent as `Authorization: Bearer {secret_key}`. > **404 Not Found — no such payment** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_found", "error": "no such payment" } > ``` > > No payment with this `payment_id` is visible to your API key — wrong id, or a payment of a different store / API key. > **409 Conflict** > > ```json theme={"dark"} > { "status": "error", "error": "payment is disputed" } > ``` > > Two possible causes: > > 1. **Active dispute on the payment** — the body carries `"error": "payment is disputed"`. The refund is blocked until the dispute is resolved; see [Disputes](/pay-in-4-custom-integration/disputes). > 2. **Concurrent refunds on the same payment** — the body carries `"error": "could not acquire lock: payment is locked"`. Another request is being processed; retry shortly with the same idempotency key. > **500 Internal Server Error** > > ```json theme={"dark"} > { "status": "error", "errorType": "internal", "error": "Internal Server Error" } > ``` > > If you send an idempotency key, retry safely per your retry policy. Without one, call `GET /api/v2/payments/{id}` first and check already-applied refunds before retrying. If `500` persists, contact the Tabby Integrations team and check [tabby-status.com](https://www.tabby-status.com/). *** ## `POST /api/v2/payments/{id}/close` Closes an `AUTHORIZED` payment (e.g. when the order is cancelled before capture). > **400 Bad Request — payment not authorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "only authorized payments can be closed" } > ``` > > Only `AUTHORIZED` payments can be closed. Check the payment status and act accordingly. > **400 Bad Request — payment already expired** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "could not cancel payment: payment was already expired" } > ``` > > The payment was `AUTHORIZED` earlier, but the authorization expired before `close` was called. No action is needed — the customer is not charged; treat the payment as expired. > **400 Bad Request — already closed** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "already closed" } > ``` > > The payment is already `CLOSED` — usually a duplicate `close` call. No action is needed. > **400 Bad Request — invalid payment id** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "invalid payment id" } > ``` > > The `payment_id` is not a valid UUID. Fix the format. > **401 Unauthorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_authorized" } > ``` > > The `Authorization` header is missing or wrong. It must be sent as `Authorization: Bearer {secret_key}`. > **404 Not Found — no such payment** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_found", "error": "no such payment" } > ``` > > No payment with this `payment_id` is visible to your API key — wrong id, or a payment of a different store / API key. > **500 Internal Server Error** > > ```json theme={"dark"} > { "status": "error", "errorType": "internal", "error": "Internal Server Error" } > ``` > > Call `GET /api/v2/payments/{id}` to check the current status before retrying. If `500` persists, contact the Tabby Integrations team and check [tabby-status.com](https://www.tabby-status.com/). *** ## Webhooks — `/api/v1/webhooks` Applies to `POST /api/v1/webhooks`, `GET /api/v1/webhooks`, `GET /api/v1/webhooks/{id}`, `PUT /api/v1/webhooks/{id}`, `DELETE /api/v1/webhooks/{id}`. Authenticate with the **Secret Key**. > [!NOTE] > > On `api.tabby.ai` a missing or malformed `Authorization` header and an unknown secret key are rejected at the edge with an **empty body** (`400` for a missing header or a non-Bearer scheme, `401` for an unknown key). The JSON bodies below are returned once the request reaches the service (today every such request on `api.tabby.sa` reaches the service). Do not rely on a response body for `400`/`401` authentication errors; branch on the status code. > **400 Bad Request — invalid webhook url** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "create payment webhook: invalid webhook url" } > ``` > > The `url` is not a valid, publicly reachable HTTPS URL. Fix the URL. > **400 Bad Request — webhook already exists** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "create payment webhook: webhook already exists" } > ``` > > A webhook with the same URL is already registered for this merchant. List the registered webhooks with `GET /api/v1/webhooks` instead of re-creating. > **400 Bad Request — max number of webhooks reached** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "create payment webhook: max number of webhooks reached" } > ``` > > The merchant already has the maximum number of registered webhooks. Remove stale ones with `DELETE /api/v1/webhooks/{id}` before adding new. > **400 Bad Request — malformed request** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "json decode: EOF" } > ``` > > The request body is empty or not valid JSON, or the webhook `id` in the path is not a valid UUID. > **401 Unauthorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_authorized", "error": "invalid secret key" } > ``` > > The `Authorization` header is missing or carries a wrong key (`api key not found` / `invalid secret key`). It must be sent as `Authorization: Bearer {secret_key}`. > **404 Not Found — no such webhook** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_found", "error": "no such webhook" } > ``` > > No webhook with this `id` exists for your merchant. List the registered webhooks with `GET /api/v1/webhooks`. > **500 Internal Server Error** > > ```json theme={"dark"} > { "status": "error", "errorType": "internal", "error": "Internal Server Error" } > ``` > > Retry per your retry policy. If `500` persists, contact the Tabby Integrations team and check [tabby-status.com](https://www.tabby-status.com/). *** ## Dispute webhooks — `/api/v1/dispute-webhooks` Applies to `POST /api/v1/dispute-webhooks`, `GET /api/v1/dispute-webhooks`, `GET /api/v1/dispute-webhooks/{id}`, `PUT /api/v1/dispute-webhooks/{id}`, `DELETE /api/v1/dispute-webhooks/{id}`. Authenticate with the **live Secret Key**; the `X-Merchant-Code` header is required on every request. See [Dispute webhooks](/pay-in-4-custom-integration/dispute-webhooks) for how they work. > [!NOTE] > > On `api.tabby.ai` a missing or malformed `Authorization` header and an unknown secret key are rejected at the edge with an **empty body** (`400` for a missing header or a non-Bearer scheme, `401` for an unknown key); a merchant code that does not belong to the key may also be answered at the edge. The JSON bodies below are returned once the request reaches the service (today every such request on `api.tabby.sa` reaches the service). Do not rely on a response body for `400`/`401` authentication errors; branch on the status code. Always call the base URL of your merchant's region. > **400 Bad Request — code is required** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "code is required" } > ``` > > The `X-Merchant-Code` header is missing. It is required on every dispute-webhook request, even when your secret key maps to a single merchant. > **400 Bad Request — invalid webhook url** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "invalid webhook url" } > ``` > > The `url` points to `localhost`, to a raw IP address, or to a host name that does not resolve in DNS. Use a publicly reachable host name (HTTPS recommended). > **400 Bad Request — webhook already exists** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "webhook already exists" } > ``` > > A dispute webhook with the same URL is already registered for this merchant (on `PUT`: the new URL belongs to another of your dispute webhooks). URLs are compared after normalisation (lower-case scheme and host, default port and trailing slash removed). List the registered webhooks with `GET /api/v1/dispute-webhooks` instead of re-creating. > **400 Bad Request — max number of webhooks reached** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "max number of webhooks reached" } > ``` > > The merchant already has 4 dispute webhooks (the limit is separate from payment webhooks). Remove stale ones with `DELETE /api/v1/dispute-webhooks/{id}` before adding new. > **400 Bad Request — malformed request** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "bad_request" } > ``` > > The request body is empty or not valid JSON, or `url` is missing, empty or not an absolute URL. Send a JSON object with a `url`, as in [Register a dispute webhook](/api-reference/dispute-webhooks/register-a-dispute-webhook). > **400 Bad Request — invalid header** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "bad_request", "errors": [ { "code": "invalid_value", "field": "$.header.title", "message": "empty value" } ] } > ``` > > `header` is present but `title` or `value` is missing, empty or invalid: `title` must be a valid HTTP header name (no spaces, up to 128 characters), `value` up to 1024 characters with no control characters. The `errors` array points at the offending field. Omit `header` entirely if you do not want one. > **401 Unauthorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_authorized", "error": "no accessible merchants found" } > ``` > > The `Authorization` header is missing or wrong (`missing Authorization header`, `missing Bearer scheme`, `api key not found`, `invalid secret key` — the last one when a public `pk_` key is sent), or the `X-Merchant-Code` does not belong to the key you authorized with (`no accessible merchants found` — also returned for an unknown code). Send `Authorization: Bearer {secret_key}` together with one of the merchant codes of that key. > **403 Forbidden — disputes have no test mode** > > ```json theme={"dark"} > { "status": "error", "errorType": "no_permission", "error": "disputes have no test mode" } > ``` > > The request was authorized with a test secret key (`sk_test_...`). Dispute webhooks exist for live payments only — use the live secret key (`sk_...`). > **404 Not Found — webhook not found** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_found", "error": "webhook not found" } > ``` > > No dispute webhook with this `id` exists for your merchant (including one that was already removed or belongs to another merchant), or the `id` is not a valid UUID. List the registered webhooks with `GET /api/v1/dispute-webhooks`. > **500 Internal Server Error** > > ```json theme={"dark"} > { "status": "error", "errorType": "internal", "error": "internal" } > ``` > > Retry per your retry policy. If `500` persists, contact the Tabby Integrations team and check [tabby-status.com](https://www.tabby-status.com/). *** ## Disputes — `/api/v1/disputes` Applies to `GET /api/v1/disputes`, `GET /api/v1/disputes/{disputeId}`, `POST /api/v1/disputes/{disputeId}/provide-evidence`, `POST /api/v1/disputes/approve`, `POST /api/v1/disputes/challenge`, `POST /api/v1/disputes/attachments/upload`. Authenticate with the **Secret Key**. See [Disputes](/pay-in-4-custom-integration/disputes) for the flow itself. > **400 Bad Request** > > ```json theme={"dark"} > { "status": "error", "errorType": "bad_data", "error": "failed to decode request" } > ``` > > The request body is malformed or a parameter is invalid (e.g. wrong `dispute_id` format, missing required fields). Read the `error` field for the specific cause. > **401 Unauthorized** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_authorized" } > ``` > > The `Authorization` header is missing or wrong. It must be sent as `Authorization: Bearer {secret_key}`. > **404 Not Found — dispute not found** > > ```json theme={"dark"} > { "status": "error", "errorType": "not_found", "error": "dispute not found" } > ``` > > No dispute with this `disputeId` is visible to your API key. Check the id against `GET /api/v1/disputes`. > **409 Conflict (provide-evidence)** > > ```json theme={"dark"} > { "status": "error", "errorType": "conflict" } > ``` > > The dispute is not in a state that accepts this operation — e.g. evidence was already submitted or the dispute has been resolved. Re-fetch it with `GET /api/v1/disputes/{disputeId}` and check its status. > **500 Internal Server Error** > > ```json theme={"dark"} > { "status": "error", "errorType": "internal", "error": "Internal Server Error" } > ``` > > Retry per your retry policy. If `500` persists, contact the Tabby Integrations team and check [tabby-status.com](https://www.tabby-status.com/). # Register a webhook Source: https://docs.tabby.sa/api-reference/webhooks/register-a-webhook post /api/v1/webhooks Creates a new webhook. Webhooks are registered per `merchant_code` and per environment: the environment is determined by the secret key you authorize the request with — a production key (`sk_...`) registers webhooks for production payments, a test key (`sk_test_...`) registers webhooks for test payments. Each `merchant_code` + key pair can have up to 4 webhooks. > [!NOTE] > > This is a public, self-service endpoint. Call `https://api.tabby.ai/api/v1/webhooks` (`https://api.tabby.sa/api/v1/webhooks` for KSA) with your secret key and the `X-Merchant-Code` header. How webhooks work, payload and retries: [Payment webhooks](/pay-in-4-custom-integration/webhooks). # Remove a webhook Source: https://docs.tabby.sa/api-reference/webhooks/remove-a-webhook delete /api/v1/webhooks/{id} Removes the specified webhook. # Retrieve a webhook Source: https://docs.tabby.sa/api-reference/webhooks/retrieve-a-webhook get /api/v1/webhooks/{id} Retrieves the specified webhook. # Retrieve all webhooks Source: https://docs.tabby.sa/api-reference/webhooks/retrieve-all-webhooks get /api/v1/webhooks Retrieves all registred webhooks. # Update a webhook Source: https://docs.tabby.sa/api-reference/webhooks/update-a-webhook put /api/v1/webhooks/{id} Updates the specified webhook. # Magento 2 Plugin Installation Source: https://docs.tabby.sa/e-commerce-platforms/magento-2/magento-2-plugin-installation Install the Tabby extension for Magento 2 with Composer, configure your API keys, and go live. > [!NOTE] > > Tabby works on the Magento 2 platform for all supported countries. The Tabby extension adds Tabby as a payment method at checkout, shows Tabby promotions on your product and cart pages, and can share your product feed with the Tabby Shop. | Requirement | Value | | - | - | | Magento | 2.4.x (`magento/framework ^103.0`) | | PHP | 7.1 or newer | | Access | Composer and command-line access to your Magento project | ## Installation Steps **Step 1** Sign up at [merchant.tabby.ai](https://merchant.tabby.ai/) (KSA: [merchant.tabby.sa](https://merchant.tabby.sa/)) and fill in all your business details. Your test API keys are then available in the Merchant Dashboard under **Business profile → Stores**. **Step 2** [Install the extension with Composer](#install-with-composer) - the recommended way to install it and to update it later. **Step 3** [Configure Tabby as a payment method](#configure-tabby-as-a-payment-method) using your test keys. **Step 4** [Place a test order and refund it](#test-your-integration) to confirm that the full flow works. **Step 5** Replace the test keys with your live keys and [deploy to production](#connect-to-the-live-tabby-environment). > [!WARNING] > > Install and test the extension in a staging environment first, and keep a backup of your Magento files and database so you can roll back. ## Install with Composer `tabby/m2-payments` is a metapackage that pulls both Tabby modules: `tabby/m2-checkout` (payment methods, promotions, webhooks) and `tabby/m2-feed` ([product catalogue](/e-commerce-platforms/magento-2/magento-2-product-catalogue)). ```bash theme={"dark"} composer require tabby/m2-payments php bin/magento module:enable Tabby_Checkout Tabby_Feed php bin/magento setup:upgrade php bin/magento setup:di:compile php bin/magento setup:static-content:deploy -f php bin/magento cache:flush ``` Confirm that both modules are enabled: ```bash theme={"dark"} php bin/magento module:status Tabby_Checkout Tabby_Feed ``` ### Update the extension Run the same sequence with `composer update` to move to the latest release: ```bash theme={"dark"} composer update tabby/m2-payments --with-dependencies php bin/magento setup:upgrade php bin/magento setup:di:compile php bin/magento setup:static-content:deploy -f php bin/magento cache:flush ``` > [!TIP] > > We recommend opening the Tabby settings in the Magento admin after an update and clicking **Save Config**, even if you change nothing. Saving re-applies your configuration to the settings the installed version supports, and registers the webhooks for any store that is missing them. > **Composer reports a higher matching version on packagist.org** > > If Composer stops with *"Higher matching version 7.0.2 of tabby/m2-payments was found in public repository packagist.org"*, your project is pinned to an older constraint. Require the version range explicitly and run the update again: > > ```bash theme={"dark"} > composer require tabby/m2-payments:^7.0 > ``` ## Other Ways to Install Composer is the supported path. Use one of these only if Composer is not available to you. > **Manual installation from an archive** > > 1. Download the [extension archive](https://storage.googleapis.com/plgns/magento2-latest.tar.gz). > > 2. Create the directory `Magento2_root/app/code/Tabby/Checkout/` if it does not exist. > > 3. Unpack the archive and move all files into `Magento2_root/app/code/Tabby/Checkout/`. > > 4. Verify that **Tabby\_Checkout** appears in the list of disabled modules: > > ```bash theme={"dark"} > php bin/magento module:status > ``` > > 5. Enable the module and rebuild: > > ```bash theme={"dark"} > php bin/magento module:enable Tabby_Checkout > php bin/magento setup:upgrade > php bin/magento setup:di:compile > php bin/magento setup:static-content:deploy -f > php bin/magento cache:flush > ``` > > > [!WARNING] > > > > The archive contains the checkout module only. The [product catalogue](/e-commerce-platforms/magento-2/magento-2-product-catalogue) module ships through Composer, and every future update has to be applied by hand. > **Adobe Commerce Marketplace** > > The extension is published as [Tabby M2 Payments](https://commercemarketplace.adobe.com/tabby-m2-payments.html). Add it to your Adobe Commerce account and install it from your Marketplace purchases. Adobe delivers the same Composer package, so the Magento commands in [Install with Composer](#install-with-composer) still apply. ## Configure Tabby as a Payment Method Sign in to your Magento 2 admin portal and go to ***Stores > Configuration > Sales > Payment Methods > Tabby Payments***. In the **Tabby Api** section: | Setting | Value | | - | - | | Merchant Public Key, Merchant Secret Key | Your test keys from the Merchant Dashboard | | Aggregate store by currency | **Yes** - the extension derives the merchant code from each store's base currency (`AE`, `SA`), which are the default codes Tabby registers for your account. Set it to **No** only if you use [your own store codes](#use-your-own-magento-store-codes). | | Capture on | **Order placed**, unless your account manager agreed on a different moment | | Debug | **Yes** while you are testing | Then enable the payment method in the **Tabby Checkout - Split in 4** section and click **Save Config**. Saving validates your keys and registers your webhooks with Tabby. > [!NOTE] > > If the changes do not take effect immediately, go to ***System > Cache Management***, click **Flush Magento Cache**, then sign out and sign back in to reset permissions. ### Manage Tabby Product Catalogue Feature The [product catalogue](/e-commerce-platforms/magento-2/magento-2-product-catalogue) lists your products on the Tabby Shop. Enable it in ***Stores > Configuration > Sales > Payment Methods > Tabby Payments > Tabby Feed***. > [!WARNING] > > The Tabby Product Catalogue feature works with **live API keys** only.  ## Test Your Integration Place an order with Tabby and refund it (create a Credit Memo) to confirm that the full flow works. When the test order and the refund go through, you can move to the live environment yourself. Tabby does not run a separate sign-off on your integration. ## Connect to the Live Tabby Environment **Step 1** Copy the live Public and Secret keys from the Merchant Dashboard under **Business profile → Stores**. If the live keys are not shown there yet, ask Tabby Partner Support at `partner@tabby.ai` (or `partner@tabby.sa` for KSA) to make them visible. **Step 2** Enter them in ***Stores > Configuration > Sales > Payment Methods > Tabby Payments*** on your production site. **Step 3** Click **Save Config**. Webhooks are registered per key pair, so this save is what creates your live webhooks - do it on production even if you copied the configuration over from staging and the keys are already in place. ## Specific Settings ### Promotions Only Mode If your checkout runs through another provider, the plugin can serve Tabby promotions only: it enables the product and cart page snippets and hides Tabby at checkout. Go to ***Stores > Configuration > Sales > Payment Methods > Tabby Payments***, and set ***Tabby plugin mode*** to ***Promotions only***:  Promo only mode ### Use Your Own Magento Store Codes By default there is nothing to send us: with **Aggregate store by currency** set to **Yes**, the extension derives the merchant code from the store's base currency (`AE` for AED, `SA` for SAR) and Tabby registers those codes for your account. Use your Magento store codes instead when you need a separate Tabby merchant code per store group, for example for separate reporting or separate payment plans per brand: **Step 1** In the Magento admin, go to ***Stores > All Stores*** and copy the **Code** of every store group you sell with.  **Step 2** Ask Tabby Partner Support at `partner@tabby.ai` (or `partner@tabby.sa` for KSA) to set them up as custom merchant codes, for example `website_store_ae`. Wait for confirmation that the codes are registered on Tabby's side. **Step 3** Set **Aggregate store by currency** to **No**. The extension now sends the store group code as the merchant code. > [!WARNING] > > Change this setting only after Tabby confirms that your codes are registered. A code Tabby does not know breaks checkout with an authorization error. ### Sell in Several Currencies If one store group sells in more than one currency, enable **Use local store currency on checkout**. The extension then charges in the currency the customer sees and appends it to the merchant code, for example `SA_SAR`. These codes have to be registered on Tabby's side too, so ask Tabby Partner Support to set them up before you turn this on. # Magento 2 Product Catalogue Source: https://docs.tabby.sa/e-commerce-platforms/magento-2/magento-2-product-catalogue Enable the Magento 2 Product Catalogue feature to list your store's products on the Tabby Shop. We've introduced an exciting new feature to the Tabby Magento 2 plugin – the **Product Catalogue Feature**, designed to help you list your store’s products seamlessly on the Tabby Shop. The feature is enabled by default in new plugin versions starting from 5.0.11. [Update the plugin](/e-commerce-platforms/magento-2/magento-2-plugin-installation#update-the-extension) with Composer: ```bash theme={"dark"} composer update tabby/m2-payments --with-dependencies php bin/magento module:enable Tabby_Checkout Tabby_Feed php bin/magento setup:upgrade php bin/magento setup:di:compile php bin/magento setup:static-content:deploy -f php bin/magento cache:flush ``` Check that the setting is enabled in the Tabby API options located at:\ **Stores -> Configuration -> Sales -> Payment methods -> Tabby Payments -> Tabby Feed -> "Share product feed with Tabby"**  **Key features:** * **Automated Product Listing**: Easily list your entire Magento 2 store inventory on the Tabby Shop without manual intervention. * **Real-Time Stock Updates**: Keep your product listings up-to-date effortlessly, as the plugin automatically syncs inventory availability and reflects out-of-stock items in real time. * **Increased Visibility**: Showcase your products to a broader audience by making them visible to all Tabby app users. * **Traffic Generation**: Drive interested shoppers directly to your Magento 2 store, increasing clicks and potential sales. # Matjrah Source: https://docs.tabby.sa/e-commerce-platforms/matjrah Connect Tabby on the Matjrah platform by entering your API keys. > [!NOTE] > > Tabby works on the Matjrah platform for the countries KSA and UAE. 1. [Register with Tabby](https://merchant.tabby.ai/) (KSA: [merchant.tabby.sa](https://merchant.tabby.sa/)) and fill all required business details to collect Tabby API Keys. 2. Fill in the keys using the instruction below:          # Odoo Plugin Installation Source: https://docs.tabby.sa/e-commerce-platforms/odoo/odoo-plugin-installation Install the Tabby module on Odoo, configure your API keys, and take the integration live. > [!NOTE] > > Tabby works on the Odoo platform for all supported countries. Odoo versions 17, 18, and 19 are supported. > [!WARNING] > > The Tabby module can be installed on self-hosted (on-premise) Odoo and Odoo.sh. Odoo Online (SaaS) does not support the installation of custom modules. ## Installation Steps *** *** *** *** *** *** ### Download the Tabby Module Tabby provides a separate module build for each supported Odoo version. Download the archive matching the major version of your Odoo installation: | Odoo version | Module download | | - | - | | Odoo 19 | [odoo19-latest.zip](https://storage.googleapis.com/plgns/odoo19-latest.zip) | | Odoo 18 | [odoo18-latest.zip](https://storage.googleapis.com/plgns/odoo18-latest.zip) | | Odoo 17 | [odoo17-latest.zip](https://storage.googleapis.com/plgns/odoo17-latest.zip) | Each archive contains the `payment_tabby` module folder — this folder is the unit of installation. ### Install the Odoo Module 1. Extract the downloaded archive and locate the `payment_tabby` module folder inside it 2. Copy the `payment_tabby` folder into your Odoo addons directory — any directory listed in the `addons_path` parameter of your `odoo.conf` (commonly a `custom_addons` folder). Make sure the copied files are readable by the user running the Odoo service 3. Restart the Odoo service 4. Activate the developer mode: go to ***Settings -> General Settings***, scroll down to **Developer Tools** and click **Activate the developer mode** 5. Go to ***Apps*** and click ***Update Apps List*** in the top menu, then confirm the update 6. In the Apps search bar remove the default filters, search for **Tabby** and click **Activate** on the "Payment Provider: Tabby" module > [!NOTE] > > **Odoo.sh:** instead of copying files to the server, add the `payment_tabby` folder to your Odoo.sh project's GitHub repository and deploy the branch — the module then appears in the Apps list after **Update Apps List**. #### Optional: Updating the Odoo Module When a new version of the Tabby module is released: 1. Download the latest archive for your Odoo version and replace the existing `payment_tabby` folder in your addons directory with the new one 2. Restart the Odoo service 3. Go to ***Apps***, remove the default filters, find the **Tabby** module and click ***Upgrade*** from the module's menu ### Configure Tabby as a Payment Method 1. Go to ***Website -> Configuration -> Payment Providers*** (the same screen is also available via ***Invoicing -> Configuration -> Payment Providers*** and ***Sales -> Configuration -> Payment Providers***) 2. Open **Tabby** in the list of providers 3. In the **Credentials** tab, enter the **Tabby Public Key** and **Tabby Secret Key** you received from your account manager 4. Set the **State** to **Enabled** 5. Click **Save** Once the provider is enabled, it is automatically published on your website and Tabby appears among the payment methods on Checkout. > [!NOTE] > > Tabby requires the order currency to match the customer's country: UAE-AED, KSA-SAR. Make sure the currency of your website/pricelist is set accordingly — Tabby will not be displayed on Checkout for unsupported currencies. ### Test Your Integration After completing your integration, make an order and refund it to ensure that everything works as expected. **Coordinate Testing with Tabby** Before deploying Tabby integration to your production site, Tabby needs to validate the correctness of the integration. Contact your account manager to coordinate this test. ### Connect to the Live Tabby Environment 1. Get your Public and Secret keys for the live environment. Ask your account manager to share the keys 2. Set your live keys in the Odoo admin panel: go to ***Website -> Configuration -> Payment Providers -> Tabby***, open the **Credentials** tab and replace the test keys 3. **Launch to Production**\ After you've connected to the live environment and we've tested your integration, you're ready to offer Tabby payment methods to your customers. ### Feedback and Support If you face any issues during installation or testing, don't hesitate to get in touch with `partner@tabby.ai` / `partner@tabby.sa` with the store link, issue description, and screenshots or screencasts of the observed behaviour. # OpenCart Plugin Installation Source: https://docs.tabby.sa/e-commerce-platforms/opencart/opencart-plugin-installation Install the Tabby plugin for OpenCart, configure your API keys, and connect to the live environment. > [!NOTE] > > Tabby works on the OpenCart platform for all supported countries. OpenCart versions 2.x, 2.1.x, and 3.x with the Journal Theme are supported. ## Installation Steps *** *** *** *** *** *** ### Install the OpenCart Extension 1. Go to ***Extensions -> Installer*** and install the extension. 2. Go to ***Extensions -> Modifications*** and click the refresh button under your profile (without re-loading this page in the browser). 3. Check the Tabby payment in the modification list. 4. Go to ***Extensions -> Extensions -> Modules*** in your OpenCart admin panel and activate Tabby Module. The example of Tabby Module settings:   #### Optional: Updating the OpenCart Extension When you want to update your current version with the new one. 1. Go to ***Extensions -> Modifications*** and delete the Tabby payment 2. Go to ***Extensions -> Installer*** and delete all files named oc3-tabby-x.x.x.ocmod.zip from install history 3. Upload and install the extension 4. Go to ***Extensions -> Modifications*** and click the refresh button under your profile (without reloading the page in the browser) 5. Check the Tabby payment in the modification list; 6. Go to ***Extensions -> Extensions -> Modules*** in your Opencart admin panel and activate Tabby Module 7. Open **Tabby Module** settings and click on Save changes button ### Configure Tabby as a Payment Method 1. Go to ***Extensions -> Extensions -> Modules*** in your OpenCart admin panel, find Tabby Module, click "Edit" and enter the **Public API Key** and **Secret API Key** for testing. 2. Go to ***Extensions -> Extensions -> Payments*** in your OpenCart admin panel and enable Tabby Installments (Split in 4). 3. Make sure the following settings are configured in any Tabby option Installments: – **Capture on – order\_placed** 4. Go to ***Design -> Layouts -> Checkout*** and add Tabby Module to Content Top **For Journal theme only:** Go to Journal → Layouts → Checkout, choose Top+, choose OpenCart (the last one) and choose Tabby Module →Tabby Checkout → Save changes. ### Test Your Integration After completing your integration, make an order and refund it to ensure that everything works as expected. **Coordinate Testing with Tabby** Before deploying Tabby integration to your production site, Tabby needs to validate the correctness of the integration. Contact your account manager to coordinate this test. ### Connect to live Tabby environment 1. Get your public and secret keys for the live environment. Ask you account manager to share the keys. 2. Set your live public and secret keys in the OpenCart admin panel, for this go to ***Extensions -> Extensions -> Modules -> Tabby Module*** in your OpenCart admin panel. 3. **Launch to Production**\ After you’ve connected to the live environment and we’ve tested your integration, you’re ready to deploy to your production environment and offer Tabby payment methods to your customers. ## Specific settings ### No Price snippet It is possible to turn on non standard Tabby snippets in ***Extensions -> Extensions -> Modules -> Tabby module (Tabby Checkout),*** parameter ***Tabby Promo theme*** should have **:non standard** specified:  Promotions theme   No price snippet # OpenCart Product Catalogue Source: https://docs.tabby.sa/e-commerce-platforms/opencart/opencart-product-catalogue Enable the OpenCart Product Catalogue feature to list your store's products on the Tabby Shop. We've introduced an exciting new feature to the OpenCart plugin – the **Product Catalogue Feature**, designed to help you list your store’s products seamlessly on the Tabby Shop. The feature is enabled by default in new plugin versions starting from 2.9.0. Install or Update the plugin to the latest version via below link:\ [[https://storage.googleapis.com/plgns/opencart-latest.zip](https://storage.googleapis.com/plgns/opencart-latest.zip)](https://storage.googleapis.com/plgns/opencart-latest.zip) Check that the setting is enabled in the Tabby API options located at:\ **Extensions -> Tabby Module -> "Tabby Product Feed"**  **Key features:** * **Automated Product Listing**: Easily list your entire OpenCart store inventory on the Tabby Shop without manual intervention. * **Real-Time Stock Updates**: Keep your product listings up-to-date effortlessly, as the plugin automatically syncs inventory availability and reflects out-of-stock items in real time. * **Increased Visibility**: Showcase your products to a broader audience by making them visible to all Tabby app users. * **Traffic Generation**: Drive interested shoppers directly to your OpenCart store, increasing clicks and potential sales. # Salesforce Source: https://docs.tabby.sa/e-commerce-platforms/salesforce Install and configure the Tabby cartridge for Salesforce Commerce Cloud. > [!NOTE] > > Tabby works on the Salesforce Commerce Cloud platform for all supported countries. ## Installation Steps *** *** *** *** *** Salesforce integration supposes adding the necessary cartridges into an SFCC reference application. Tabby cartridge package contains 3 folders: 1. int\_tabby\_core – Tabby core implementations both for Sitegenesis and SFRA 2. int\_tabby\_sfra – Tabby SFRA changes/implementations 3. bm\_tabby – Tabby Business Manager module > [!WARNING] > > Compatibility: SFRA v6.0.0 Compatibility Mode 21.7 ### Cartridges Installation #### Adding Cartridges in Salesforce Commerce Cloud Studio 1. Open package.json and ensure the base path mentioned is correctly resolved (please provide the correct path to app\_storefront\_base) 2. Open the command prompt and run ‘npm install’ (assuming node.js (12.21.0 and above) is installed) to install all of the local dependencies 3. Run the “npm run compile” 4. Create the dw\.json file: ``` { "hostname": "your-sandbox-hostname.demandware.net", "username": "yourlogin", "password": "yourpwd", "code-version": "version_to_upload_to" } ``` 5. Upload this code to the environment using the command ‘npm run uploadCartridge’, that will upload the **int\_tabby\_core** , **int\_tabby\_sfra** and **bm\_tabby** cartridges to the sandbox you specified in dw\.json file #### Activating Cartridges in Business Manager Before Tabby functionality can become available to Reference Architecture, the cartridges need to be added to the cartridge path of the site. In order to do this, follow the following instructions: * Log into Business Manager * Navigate to *Administration -> Sites -> Manage Sites* * Click on the site name * Go to the "Settings" tab * In the textbox cartridges add **int\_tabby\_sfra:int\_tabby\_core** before **app\_storefront\_base** > [!TIP] > > example: **int\_tabby\_sfra:int\_tabby\_core: app\_storefront\_base** * Click **Apply** * Repeat steps 3 to 5 for each site that is to use Tabby * To run the Jobs and have tabby BM module, Navigate to *Administration -> Sites -> Manage Sites* * Go to the "Manage the Business Manager site” tab * In the textbox cartridges add **int\_tabby\_core:bm\_tabby** to the start ### Configuration #### Importing Meta Data with a single Site Import All import files are located in the import folder (metadata) within the cartridge installation pack. To import necessary Tabby settings: * Open the metadata folder from the repository structure (open jobs.xml) and change the references of RefArchGlobal to your site id; * Open the metadata/sites folder from the repository structure and change the folder name from RefArchGlobal to your site id. If you have multiple sites with Tabby, then copy RefArchGlobal folder and rename it with your other site id(s). * Open the metadata/sites/YourSiteID/preferences.xml file from the repository structure; * Fill in **tabbyServiceCredentialPublicKey** and **tabbyServiceCredentialSecretKey** with keys provided by your account manager from Tabby; * Fill in **tabbyWebhooksTitle** and **tabbyWebhooksValue** values; * Compress the metadata folder from the repository structure; * Log in to the Business Manager and navigate to *Administration -> Site Development -> Site Import & Export*, upload the zipped folder, go back and use the import button to import the file; * After a successful import, the entire configuration requirements needed for Tabby are available according to your Tabby account data. #### Managing Tabby Custom Site Preferences In Business Manager, navigate to the *Site -> Merchant Tools -> Site Preferences -> Custom Preferences*. A custom site preference group "Tabby" is available. Please select it and edit the attributes according to your Tabby account data. | | | | | - | - | - | | **Site Preference** | **Description** | **Values** | | Is Tabby Enable | This field represents the enable/disable tabby implementation. | Boolean: True/FalseDefault: True | | Tabby Environment | This field represents the type of the environment. | Enum of String: Development -> Development instancesProduction -> Production instancesDefault: Development | | Service Credential Public Key | This field represents the tabby service credential public key (pk\_...). | String: (Mandatory) The public API key that Tabby provided to you | | Service Credential Secret Key | This field represents the tabby service credential secret key (sk\_...). | String: (Mandatory) The secret API key that Tabby provided to you | | Is Tabby Installments Enable | This field represents the Installments payment method is enabled. | Boolean: True/False Default: True | | Is Tabby Credit Card Installments Enable(Deprecated) | This field represents the Credit Card Installments payment method is enabled. | Boolean: True/False Default: False | | Is Tabby Pay Later Enable | This field represents the Pay Later payment method is enabled. | Boolean: True/False Default: False | | Immediate Capture On Authorize | This field represents if the Immediate Capture on Authorize is enabled. If Yes -> Capture request will be triggered automatically after success Authorization. If No -> Tabby-Capture scheduled job will send the capture request. | Boolean: True/False Default: True | | Installments Count | This field represents Installments counts. | Integer:Default: 4 | | Tabby Merchant Code Per Country | This field represents the Merchant codes per country in a JSON format: \{ "AE": "uae","SA": "ksa","KW": "kwt"} | Text (JSON) \{ "AE": "uae","SA": "ksa","KW": "kwt"} - need to define Site countries only. | | Card Size | This field represents the tabby card size, can be narrow or wide, depending on the width | Enum of String: Narrow/Wide Default: Narrow | | Card Theme | This field represents the tabby card theme, can be default or black, depending on the styles | Enum of String: Default/Black Default: Default | | Webhooks Title | This field represents the arbitrary header name to sign the request for the webhooks.Will be used to verify webhook requests that tabby system sends. | String: | | Webhooks Value | This field represents the random string to sign the request for the webhooks.Will be used to verify webhook requests that tabby system sends. | String: | #### Managing Tabby Payment Processor In Business Manager select the required site from the dropdown and navigate to *Merchant Tools -> Ordering -> Payment Processors*. #### Managing Tabby Payment Methods In the integration package, a payment method definition is provided in the file that you have already imported 1. Navigate to *Merchant Tools -> Ordering -> Payment Methods*; 2. Enable/Disable the Payment Method under *Ordering -> Payment Methods* section that needs to be activated. Repeat these steps for all the Payment Methods that are required to be supported on the site. #### Managing Jobs In the integration package, a job definition is provided in the import zip (metadata.zip) file (jobs.xml). Make sure to associate the jobs with the respective merchant site. *Tabby-CaptureShippedOrders* It captures the payments that are authorized by Tabby. This job fetches the orders which have confirmationStatus=CONFIRMED, paymentStatus=NOTPAID, shippingStatus=SHIPPED and custom.tabbyPaymentStatus: AUTHORIZED. > [!TIP] > > Script file: int\_tabby\_core/cartridge/scripts/jobs/capture.js If the site preference “**Immediate Capture On Authorize**” is set to "True", it is not necessary to enable this job. *Tabby-RegisterWebhooks* It registers the Webhooks for the sites defined by merchant*codes from the "Site preference" tab. Each* merchant\_code needs to be registered separately. This job is a one time job to register the Webhooks. It is important to fill in “Webhooks Title” and “Webhooks Value” in the "Site Preferences" before running this job. > [!TIP] > > Script file int\_tabby\_core/cartridge/scripts/jobs/webhooks.js *Tabby-Feedback* It processes the TabbyFeedback custom objects that are saved within the TabbyWebhooks-Process endpoint. Custom object details are to compare with the order payment statuses. If there is a difference between the order/payment status, the job will synchronize the order/payment statuses based on the TabbyFeedback details. > [!TIP] > > Script file int\_tabby\_core/cartridge/scripts/jobs/feedback.js #### Tabby Business Manager Module The cartridge for Business Manager module bm\_tabby is present in the installation package. It helps processing capture and refund APIs. To enable/activate it, do the following: 1. Navigate to *Administration -> Organization -> Roles & Permissions*; 2. Click **Administrator** in the Roles; 3. Click **Business Manager Modules**; 4. Select the sites where you have Tabby; 5. Click **Apply**; 6. Scroll to *Ordering -> Tabby Orders* and check the checkbox; 7. Click **Update** at the bottom of the page; Once it is enabled, “Tabby Orders” will be placed under *Merchant Tools > Ordering* # Salla Source: https://docs.tabby.sa/e-commerce-platforms/salla > [!NOTE] > > Tabby works on the Salla platform for the KSA only with SAR as currency. 1. [Register with Tabby](https://merchant.tabby.ai/) (KSA: [merchant.tabby.sa](https://merchant.tabby.sa/)) and fill all required business details to collect Tabby API Keys. 2. Fill in the keys using the instruction below:        # Shopify Plugin Installation Source: https://docs.tabby.sa/e-commerce-platforms/shopify/shopify-plugin-installation Install the Tabby app for Shopify, configure webhooks and settings, and go live. > [!NOTE] > > Tabby works on the Shopify platform for all supported countries. Currency conversion may apply for multi-currency stores, check **Multi-Currency Processing and Currency Conversion** below. ## Installation Steps No time to read or have questions on any of the steps? Watch video guide on\ **How to set up Tabby on Shopify:** **1. Register with Tabby** [Register with Tabby](https://merchant.tabby.ai/) (KSA: [merchant.tabby.sa](https://merchant.tabby.sa/)) and fill all required business details to collect Tabby Live API Keys. **2. Install the Tabby Payment App** To install Tabby Payment App, [click here](https://apps.shopify.com/tabby-payment-1). After the installation Shopify opens the Tabby payment method page. **3. Add your API keys** On the Tabby payment method page click **More actions → Manage**. Copy the **Live Public** and **Secret keys** from the [Merchant Dashboard](https://merchant.tabby.ai/business-profile/stores) (KSA: [merchant.tabby.sa](https://merchant.tabby.sa/business-profile/stores)) and paste them in the necessary fields. Leave the **Webhook Verification Code** empty, you will get it in Step 5. Click **Save**. Keep this page open — you come back to it in Step 5. > [!NOTE] > > **If you closed the Tabby page** > > Open **Settings → Payments** and in **Additional payment providers** click **Add**. > >  > > Stay on the **Search by payment method** tab and search for **Tabby**. > >  > > **More actions → Manage** opens the settings page again. > >  **4. Set up Shopify Webhooks** Tabby requires Shopify Webhooks to update the order number, fetch the phone number and order lines from Shopify, and read product details such as category and brand from your catalogue. Open **Settings → Notifications → Webhooks** and click **Create webhook**. Add 6 webhooks: | event | Callback URL | Format | API version | | - | - | - | - | | Transaction creation | `https://sfy-payment.tabby.ai/api/webhook/transaction/create` | JSON | Latest | | Order creation | `https://sfy-payment.tabby.ai/api/webhook/order/create` | JSON | Latest | | Checkout creation | `https://sfy-payment.tabby.ai/api/webhook/checkout/create` | JSON | Latest | | Checkout update | `https://sfy-payment.tabby.ai/api/webhook/checkout/create` | JSON | Latest | | Product creation | `https://sfy-payment.tabby.ai/api/webhook/product/create` | JSON | Latest | | Product update | `https://sfy-payment.tabby.ai/api/webhook/product/update` | JSON | Latest | Use only the **latest** API version on every webhook. **5. Add the Webhook Verification Code** Copy the Webhook Verification Code which is generated after the first Webhook creation.  Webhook Verification Code Paste the code in **Settings → Payments → Additional payment providers → Tabby → More actions → Manage → Webhook Verification Code** and click **Save**. **6. Activate Tabby as a payment method** The API keys and the Webhook Verification Code are saved now, so Tabby can be switched on. On the Tabby settings page click **Back to Settings** to return to the Tabby payment method page and click **Activate**. **7. Check the required settings** 1. Make sure **Test mode** is off. The toggle sits on the Tabby payment method page — **Settings → Payments → Additional payment providers → Tabby**. Tabby does NOT pay out for test / playground orders ever, even if a live customer places a real order while Test mode was on.  2. Email and phone number are required for Tabby payment method. Please enable the following settings in your admin panel:   > [!WARNING] > > If you have installed Webhooks earlier and have issues with updating order numbers - please, make sure all 6 webhooks are set up and you are using the latest available API version. Kindly refer to the > [Webhook API Version Update.](/e-commerce-platforms/shopify/shopify-plugin-installation#webhook-api-version-update) ### Webhook API Version Update Here **Webhook Update Demonstration** can be reviewed: Shopify Webhooks are created for the particular API version. Shopify releases several versions per year, so it means your initial API version settings will inevitably expire. Tabby makes the necessary changes to support the latest updated API versions, but this also requires a manual action from the partner's end. You need to go to Settings -> Notifications -> Webhooks and choose the **latest** API version from the dropdown of a particular Tabby Webhook. ## Add Tabby Snippets   Tabby snippets let your customers know they can split their payments with Tabby in your store. Snippets appear on the product and cart pages and may help improve conversion. To add Tabby snippets to your product and cart pages, please follow our detailed instructions and video manuals here: [Tabby Snippets for Shopify](/e-commerce-platforms/shopify/shopify-snippets) > [!WARNING] > > Please note that snippets alone do not activate Tabby as a payment method in your store and don’t affect payment processing. Before adding snippets, make sure Tabby is already integrated and visible on your checkout page. ## Tabby is live now! Tabby orders are captured automatically. Refunds can be initiated from Shopify admin panel and will reflect on Tabby Merchant Dashboard, Customer Portal and Settlements. ## Limitations and Known Constraints ### Language Settings The locale for Tabby Hosted Payment Page and Payment Method will be set based on the store's language, Tabby supports English and Arabic. If the locale is not supported, English is used by default. You can configure your store's language in your Shopify admin under Settings -> Languages. ### Order Number Update When an order is placed in Shopify, a payment id is set in the Shopify order timeline information under the "payment was processed on tabby." section, within the "**Information from the gateway**" subsection. This payment id is set in the Tabby order's "reference\_id" (order number). Shopify does not share the [Shopify order number](https://community.shopify.com/c/shopify-apps/getting-the-order-number/td-p/1429420) for this payment app integration and Tabby is not able to match the Shopify order number or order number in the Tabby order. The Webhooks allow to update order number or Tabby Merchant Dashboard, Customer Portal and Settlements.  ### Order Editing Please note that order details editing (as order lines, customer details, etc.) will be reflected in Shopify Dashboard only and will not updated for the payment on Tabby Merchant Dashboard, Customer Portal and Settlements. ### Order Lines Shopify doesn't share order data via the Payment App and Shopify does not share any order line data for non-product order lines, e.g. VAT/taxes, discounts, surcharges, tip, etc. Tabby orders will have necessary Order Lines data after Webhook installation. ### Order Tagging Tabby Payment App cannot currently set tags on a Shopify order. You can use the [Shopify Flow app](https://apps.shopify.com/flow) (free and [available on all Shopify plans](https://help.shopify.com/en/manual/shopify-flow)) which can set order tags within a flow for Tabby orders. ### Payment Method Presentation The Tabby payment method name is provided with English and Arabic translations out of the box. To reorder or hide payment methods in Shopify checkout, use Shopify's [checkout customizations](https://help.shopify.com/en/manual/checkout-settings/checkout-customization) powered by [Payment Customization Functions](https://shopify.dev/docs/api/functions/latest/payment-customization) — either via a Functions-powered app from the Shopify App Store, or via Checkout Blocks (Shopify Plus). The legacy checkout scripts (Script Editor) [stopped executing on June 30, 2026](https://shopify.dev/changelog/shopify-scripts-will-be-deprecated-on-june-30-2026) and can no longer be used for payment method customization. Customizations of the Tabby payment method presentation (logo, footer text) are currently supported only for Shopify Plus merchants. If you implement your own custom snippet, notify your Tabby account manager so we can keep your integration in sync with upcoming changes. ### Domain Filtering Payment App uses a different hosted payment page URL: `checkout.tabby.ai` (UAE) or `checkout.tabby.sa` (KSA). Merchants that filter this domain in Google Analytics will need to update their GA configurations accordingly. ### Market Limitation Tabby Payment App accept orders only for the billing addresses in the markets of KSA and UAE. Customers for non-supported regions based on the order's currency are shown an error message on page load that the Tabby order cannot be processed. We recommend limiting Tabby's visibility at checkout to the countries where your store works with Tabby — has a signed contract and an active integration. You can hide Tabby for other countries using Shopify's [checkout customizations](https://help.shopify.com/en/manual/checkout-settings/checkout-customization) (available on all Shopify plans), so customers from non-supported regions don't see the error message. ### Multi-Currency Processing and Currency Conversion Tabby processes payments in the following currencies: SAR (SR) and AED (Dhs), which must correspond to the customer's billing address. Tabby Payment App is compatible with all Shopify stores, regardless of their base currency. If the store’s base currency differs from the customer’s billing currency, currency conversion will be applied, and the converted amount will be displayed at Tabby checkout. Customer transactions and merchant settlements will be processed in the customer’s billing currency. Currency conversion is also applied between supported currencies if the store’s base currency differs from the customer’s billing currency. Please note that to enable conversion, the “Enable Multi-Currency Processing” option must be selected in the Tabby settings. ### Inventory Lock Limitation and Inventory Stock Check When a customer places an order with an alternative payment integration, such as Tabby Payment App, Shopify redirects to the payment method but does not lock the order's product inventory and thus stock inventory cannot be guaranteed. However, Tabby can check inventory immediately after payment is completed but before the order is created in Shopify. If an item goes out of stock during the payment process, the funds will be refunded to the customer, and the Shopify order will not be created. To enable this functionality, please turn on the **Check Inventory Stock** feature in Tabby settings in your Shopify admin panel. Note that this feature only works when the following settings are applied to your products in Shopify settings: * **Inventory tracking** is enabled * **Continue selling when out of stock** setting is disabled ### Additional Request of the Phone Number Shopify may not share the customer's phone number with payment integrations at checkout (for example, when email is required instead of phone). Tabby fetches the phone number via the Shopify Webhooks set up during the [Installation Steps](/e-commerce-platforms/shopify/shopify-plugin-installation#installation-steps). If the webhooks are not set up, Tabby will ask the customer to enter the phone number again within our Hosted Payment Page. ### Checkout Error Limitation *"There was an issue processing your payment. Try again or use a different payment method." –* this message can happen for tech/authorization reasons only. The most common reason (for new integrations) is that the API keys haven't been populated fully. You can check the API credentials at [merchant.tabby.ai](https://merchant.tabby.ai/) (KSA: [merchant.tabby.sa](https://merchant.tabby.sa/)). Also, this message appears if country in shipping or billing address is different from country (countries) that your Tabby account is signed for. It can also appear if your integration hasn't been set up on the Tabby side yet — in this case contact Tabby partner support at `partner@tabby.ai` / `partner@tabby.sa`.  ### Redirection for Background Pre-Scoring Rejection Tabby now redirects the customers to our internal rejection screens where we explain the reason and advise how to avoid this rejection if possible. Customers may see the general rejection explanation (can't be avoided), too high cart total and too low cart total rejections (can be skipped by adjusting the cart total). ### The Amount Paid by the Customer to Tabby Doesn't Match the Shopify Order Total If customers update a related, but different Shopify session separately from the Shopify session used to place the Tabby order, the order totals between the Shopify and Tabby orders may not match, but the amount "Paid by customer" for the Shopify order should always match the Tabby order total. While not ideal, this is working as expected and designed. Care should be taken to only fulfill order line items paid for by the customer. ### Feedback and Support If additional support is needed, email `partner@tabby.ai` / `partner@tabby.sa` (please include your Shopify store name and add your Account Manager in CC) for specific Tabby support. Customer Tabby Support available at `help@tabby.ai` / `help@tabby.sa` [[https://community.shopify.com](https://community.shopify.com)](https://community.shopify.com) is a good place for Shopify questions, support, and feedback, in addition to [[https://help.shopify.com/](https://help.shopify.com/)](https://help.shopify.com/) # Shopify Snippets Source: https://docs.tabby.sa/e-commerce-platforms/shopify/shopify-snippets Add Tabby promotional snippets to your Shopify product and cart pages. Tabby snippets let your customers know they can split their payments with Tabby in your store. Snippets appear on the product and cart pages and may help improve conversion. > [!WARNING] > > Please note that snippets alone do not activate Tabby as a payment method in your store and don’t affect payment processing. Before adding snippets, make sure Tabby is already integrated and visible on your checkout page. ## Adding Tabby Snippets on Product Pages Here you may see a video on **How to add Tabby snippet to product pages on Shopify**: 1. In your Shopify admin, go to **Online Store → Themes**. 2. Click the **Customize** button on your current theme. 3. At the top of the screen, click **Home page**, then choose **Products → Default product**. 4. In the left-side menu, add a block, click **Apps** and choose **Tabby Installments**. > [!WARNING] > > If your theme doesn’t allow adding a block, please navigate to > [Manual Snippet Installation](#manual-snippet-installation) 5. Drag the **Tabby Installments** block to a desirable position — ideally just below the price. 6. Click **Save** and check your storefront to make sure the snippet appear properly. If the installments don’t display correctly, your theme may not be fully compatible with default Tabby snippets. In that case, we recommend using “No-price” snippets, which don’t show installment amounts. To activate “No-Price” snippets, please click on the **Tabby Installments** block and switch the **Enable “No-price” Snippet** toggle to the **on** position. Don't forget to save the changes. ## Adding Tabby Snippets on Cart Page Here you may see a video on **How to add Tabby snippet to cart page on Shopify**: 1. In your Shopify admin, go to **Online Store → Themes**. 2. Click the **Customize** button on your current theme. 3. At the top of the screen, click **Home page**, then choose **Products → Cart**. 4. In the left-side menu, add a block, click **Apps** and choose **Tabby Cart Page**. > [!WARNING] > > If your theme doesn’t allow adding a block, please navigate to > [Manual Snippet Installation](#manual-snippet-installation) 5. Drag the **Tabby Cart Page** block into a suitable spot. 6. Click **Save** and verify that the snippets appear correctly on the storefront. If installments are incorrect, you can switch to a simplified "No-price" version of the snippet. To activate "No-price" snippets, please click on the **Tabby Cart** block and switch the **Enable “No-price” Snippet** toggle to the **on** position. Don't forget to save the changes. This is how the snippets look on the storefront: #### Default snippet   #### "No-price" snippet   ## Manual Snippet Installation Some Shopify themes may not support adding blocks on product pages. In such cases, the Tabby snippets need to be added manually. For this, you should create a file with the snippet code in your theme and render it on your product page. ### Step 1. Create a file 1. In the Shopify Admin panel, click **Online Store** → **Themes** 2. On the Themes page, click the **“…”** → **Edit code** 3. In the navigation menu on your left, click the **Snippets** folder and click the symbol of adding a new file 4. Name it **tabby-installments.liquid** ### Step 2. Add code 1. Copy one of the following codes and paste it into the file you've created. We offer two variants of snippets: **default snippets** with installments and **"No-price" snippets**. The "No-price" code should be used if the default snippets don't work properly, which may be the case for some themes. Please try the "default" code first. **Default Snippet Code** ```HTML theme={"dark"} {%- assign lang = localization.language.iso_code -%} {%- assign currency = cart.currency.iso_code -%}
``` **No-price Snippet Code** ```HTML theme={"dark"} {%- liquid assign lang = localization.language.iso_code -%} ``` 2. Check the variables in the code: *For default snippet*: Put your **Public API Key** in the line 9 replacing **YourAPIkey** text. *For No-price snippet*: Put your **Public API Key** in the line 17. In the line 18, put your **merchant code** based on your store currency, instead of the text **yourMerchantCode**. Please note that you can find your Public API key in the Tabby Dashboard or in the initial email from your Tabby manager. 3. Save the changes. ### Step 3. Render the Snippet 1. First, find the file in your theme that is responsible for the page layout. The exact file name may vary depending on the theme, but it's typically located in the **Sections** folder. For product page, the most common file names are *product.liquid*, *main-product.liquid*, *price.liquid*, etc. For cart page, it may be *cart.liquid* or one of the files included in *templates/cart.json* (e.g., *main-cart-items.liquid*, *cart-footer.liquid*) 2. Once you find appropriate file in your theme, place the following command near the price element: ``` {% render 'tabby-installments'%} ``` 3. Click Save and check if the snippets appear correctly in the desired place on the storefront. If you notice any issues with the content of the default snippet, go back to [Step 2](#step-2-add-code) and replace the code in the **tabby-installments.liquid** file with the “No-price” option. After saving the changes, the "No-price" snippets should appear on the storefront. # WooCommerce Plugin Installation Source: https://docs.tabby.sa/e-commerce-platforms/woocommerce/woocommerce-plugin-installation Install the Tabby plugin for WooCommerce, configure your API keys, and go live. > [!NOTE] > > Tabby works on the WooCommerce platform for all supported countries. ## Installation Steps *** *** *** *** *** ### Manual Plugin Installation 1. Go to “Plugins” in your WooCommerce admin panel 2. Click “Add new” -> “Upload plugin” 3. Choose the downloaded archive in zip format 4. Click “Install now” and click “Activate plugin” ### Configure Tabby as a Payment Method 1. Go to ***WooCommerce -> Settings -> Tabby API*** and enter the **Public API Key** and **Secret API Key** 2. Save plugin changes 3. Enable Tabby as a Payment method in ***WooCommerce -> Settings -> Payments***, parameter "**Pay later with Tabby**" Find below the example of Tabby API settings:    ### Enable Autoupdates 1. Find Tabby in the list of installed plugins 2. Click “Enable auto updates” ### Test Your Integration > [!WARNING] > > This testing is performed on Production keys -> it means you will not be able to complete testing payment using testing credentials. If you want to get Testing keys - kindly request `partner@tabby.ai` / `partner@tabby.sa` to provide you with them. Follow the steps to verify the integration is set up properly: 1. Add any product to a cart 2. Go to your checkout page 3. Enter Tabby [Test Credentials](/testing-guidelines/testing-credentials) based on the country on the checkout page (**important** to use email and phone number for **positive flow** which are provided in Tabby documentation) 4. Make sure that you've been redirected to Tabby HPP 5. You can't proceed with the payment using the test credential - it is an expected result since you already use the Production keys 6. Click on **Back to Store** button, confirm cancellation and make sure that you've been successfully redirected back to the checkout page. If all steps are passed successfully - you are live and customers can pay with Tabby! #### Tabby is not Present on Checkout In case Tabby is not present on Checkout - kindly review the following possible options: * needed countries are enabled in ***WooCommerce -> Settings -> Tabby API***, parameter "**Allowed Countries**" * Tabby payment method is enabled in ***WooCommerce -> Settings -> Payments***, parameter "**Pay later with Tabby**" * country-currency pair is specified correctly (UAE-AED, KSA-SAR, etc.). If not - install any currency switcher available on the WordPress store to ensure multi-currency support (it is a [known constraint](/e-commerce-platforms/woocommerce/woocommerce-plugin-installation#multi-currency-support)) * option "**Payment gateway**" is selected in ***WooCommerce -> Settings -> Tabby API***, parameter "**Plugin mode**" If these parameters are set up accordingly and Tabby still does not appear on Checkout - kindly email `partner@tabby.ai` / `partner@tabby.sa` (please, include your WooCommerce store name and add your Account Manager in CC) for specific Tabby support. ## Specific Settings ### No Price snippet It is possible to turn on non standard Tabby snippets in ***WooCommerce -> Settings -> Tabby API***, parameter ***Tabby promotions theme*** should have **:non standard** specified:  Tabby promotions theme   No price snippet ### Promotions Only Mode It is possible to set Tabby plugin for promotion purposes only: PDP / Cart snippets will be displayed, but Tabby will not be present among payment methods on Checkout. For this setup go to ***WooCommerce -> Settings -> Tabby API***, parameter ***Plugin mode*** and select ***Promo only*** there:  Promo only mode ## Limitations and Known Constraints ### Multi-Currency support In case you integrate Tabby in different countries - please, make sure that you use an automated currency converter on the checkout as Tabby requires currency to match the customer’s country depending on the shipping country. You can use any currency switcher plugin available on the WordPress store. > [!NOTE] > > Tabby currently operates in KSA and UAE. If you use a currency switcher plugin, make sure to add Tabby payment method for every supported currency. To do that, go to ***Admin → Multi currency → Checkout → Checkout currency*** and add Tabby payment method next to each supported currency.  Multi currency → Checkout → Checkout currency ### Logo size Issue The issue is relevant for WordPress sites with WooCommerce. It is connected with the different page templates and themes using. Because the Tabby plugin can't control the specific theme usage at partners' sites, you may need to modify the CSS file to have the following parameters: `max-width: 50px;`\ `max-height: 30px;` ### Feedback and Support If you face any issues during testing, don't hesitate to get in touch with `partner@tabby.ai` / `partner@tabby.sa` with the store link, issue description, and screenshots or screencasts of the observed behaviour. # WooCommerce Product Catalogue Source: https://docs.tabby.sa/e-commerce-platforms/woocommerce/woocommerce-product-catalogue Enable the WooCommerce Product Catalogue feature to list your store's products on the Tabby Shop. We've introduced an exciting new feature to the Tabby WooCommerce plugin – the **Product Catalogue Feature**, designed to help you list your store’s products seamlessly on the Tabby Shop. The feature is enabled by default in new plugin versions starting from 5.0.8. Update the plugin or download the latest version from the WordPress store: [[https://wordpress.org/plugins/tabby-checkout](https://wordpress.org/plugins/tabby-checkout)](https://wordpress.org/plugins/tabby-checkout) or by enabling auto-updates on **Plugins section**.  Check that the setting is enabled in the Tabby API options located at:\ **WooCommerce -> Settings -> Tabby API -> "Share product feed with Tabby."**  **Key features:** * **Automated Product Listing**: Easily list your entire WooCommerce store inventory on the Tabby Shop without manual intervention. * **Real-Time Stock Updates**: Keep your product listings up-to-date effortlessly, as the plugin automatically syncs inventory availability and reflects out-of-stock items in real time. * **Increased Visibility**: Showcase your products to a broader audience by making them visible to all Tabby app users. * **Traffic Generation**: Drive interested shoppers directly to your WooCommerce store, increasing clicks and potential sales. # Zid Source: https://docs.tabby.sa/e-commerce-platforms/zid > [!NOTE] > > Tabby works on the Zid platform for the countries KSA and UAE. 1. [Register with Tabby](https://merchant.tabby.ai/) (KSA: [merchant.tabby.sa](https://merchant.tabby.sa/)) and fill all required business details to collect Tabby API Keys. 2. Fill in the keys using the instruction below:       # Tabby docs for AI & LLMs Source: https://docs.tabby.sa/introduction/ai-tools Use the Tabby documentation with ChatGPT, Claude, Cursor and other AI tools — via llms.txt, per-page Markdown, and the hosted MCP server. These docs are built to be read by AI assistants and coding agents, not just people. Every page is available as plain Markdown, the whole site is exposed as `llms.txt`, and there is a hosted MCP server you can connect your AI tool to. ## Read any page as Markdown Use the **Copy page** / **Open in ChatGPT** / **Open in Claude** menu at the top of any page, or append `.md` to a docs URL to get the raw Markdown: ``` https://docs.tabby.ai/pay-in-4-custom-integration/quick-start.md ``` ## llms.txt We publish the [llms.txt standard](https://llmstxt.org) so an LLM can discover and ingest the documentation in one pass: * **[llms.txt](https://docs.tabby.ai/llms.txt)** — an index of every page with titles and descriptions. * **[llms-full.txt](https://docs.tabby.ai/llms-full.txt)** — the full content of the documentation in a single file. Paste either link into your AI tool when you want it to reason over the whole integration guide. ## MCP server Tabby's documentation is also available as a [Model Context Protocol](https://modelcontextprotocol.io) server, so AI tools can search and read the docs directly: ``` https://docs.tabby.ai/mcp ``` The server exposes a documentation search tool and a read-only filesystem of every page and the OpenAPI spec — your assistant can look up endpoints, payloads and integration steps on demand. ```json Cursor theme={"dark"} { "mcpServers": { "tabby-docs": { "url": "https://docs.tabby.ai/mcp" } } } ``` ```bash Claude Code theme={"dark"} claude mcp add --transport http tabby-docs https://docs.tabby.ai/mcp ``` > [!NOTE] > > This MCP server covers the **documentation** only. It does not call the Tabby API or move money — > use the [API Playground](/api-reference/overview) and your secret key for live requests. # F.A.Q. Source: https://docs.tabby.sa/introduction/faq This page gives you answers to the most common questions about Tabby operating. ## Where can I find credentials to use Tabby? ### Live Credentials Live API keys can be obtained: * from Tabby Merchant Dashboard for **Self-Hosted plugin** integration listed in the Quick Start section. * by contacting Tabby account manager after QA testing carried out by Tabby team is completed for **Custom API** integration. Also refer to [Test Credentials](/introduction/faq#test-credentials). If the credentials are missing or wrong, Tabby will respond with `401 Not authorized`. Error codes for a session creation request can be found here: [API Error Codes](/api-reference/response-codes). In this case kindly contact Tabby Partner Support at `partner@tabby.ai` (or `partner@tabby.sa` for KSA) or reach out to your assigned Business Manager. When doing so, kindly include: 1. your store name as registered with Tabby; 2. your integration details: * website URL or app name (for online integrations); * description of offline integration, if applicable. ### Test Credentials To test your custom integration with Tabby you need to use the following: * your testing keys, starting with `pk_test / sk_test`; * download the Postman API Collection from here: [Postman API Collection](https://docs.tabby.ai/testing-guidelines/postman-api-collections); * or use Tabby endpoints provided here: [API Playground](https://docs.tabby.ai/api-reference/); > [!NOTE] > > Tabby identifies live or test environment based on the keys used. The endpoints for test are the same as for live. * review the [Full Testing Checklist](https://docs.tabby.ai/pay-in-4-custom-integration/full-testing-checklist); * check the Testing Scenarios based on your type of integration: * Online Custom integration: [Testing Scenarios for Online Custom Integration](https://docs.tabby.ai/testing-guidelines/testing-credentials) * Offline Integrations -> POS: [Testing Scenarios for POS](https://docs.tabby.ai/offline-payment-methods/pos-integration#testing-scenarios) * Offline Integrations -> Custom Payment Links: [Testing Scenarios for Custom Payment Links](https://docs.tabby.ai/offline-payment-methods/custom-payment-links#testing-scenarios) ## Does Tabby provide any sandbox environment for testing? Tabby provides a single environment for both integration and live launch. Use `api.tabby.ai` for UAE, or `api.tabby.sa` for KSA (see [Base URLs](/api-reference/overview#base-urls)). Payments can be created: * in test mode using **Test API keys** from merchants' DEV and Stage environments; * in live mode using **Live API keys**. Live keys are shared after development is completed and QA by Tabby team is done and confirmed. > [!NOTE] > > Checkout, Payments and Payment webhooks are available for both test and live payments, while Disputes and Dispute webhooks are supported for live payments only. ## What should I do if my platform is not present in the list of supported platforms? In such case the integration is considered an [Online Custom Integration](https://docs.tabby.ai/pay-in-4-custom-integration/quick-start) - kindly follow its integration guidelines. ## What is the difference between Close and Capture API calls? The main difference between these API calls is that when the **CLOSE** API call is used - you cancel the payment from your side, and Tabby will not release funds to you. However, when you use **CAPTURE** API call - you confirm all is good with the order from your side, Tabby will release funds to you. * Close API call cancels the payment without capturing it. The downpayment is returned to the customer, the payment gets status `CANCELLED` on Tabby Merchant Dashboard: [https://docs.tabby.ai/api-reference/payments/close-a-payment](https://docs.tabby.ai/api-reference/payments/close-a-payment) * Capture API call captures the payment, the payment gets status `CAPTURED` on Tabby Merchant Dashboard: [https://docs.tabby.ai/api-reference/payments/capture-a-payment](https://docs.tabby.ai/api-reference/payments/capture-a-payment) ## How can I verify Payments via Webhooks? Main article: [Webhooks](https://docs.tabby.ai/pay-in-4-custom-integration/webhooks). There are 3 ways to secure the Webhook notifications: * allow these IP-addresses for Webhooks: ``` 34.166.36.90 34.166.35.211 34.166.34.222 34.166.37.207 34.93.76.191 34.166.128.182 34.166.170.3 34.166.249.7 ``` * add static `AUTH HEADER` during webhook registration: [https://docs.tabby.ai/api-reference/webhooks/register-a-webhook](https://docs.tabby.ai/api-reference/webhooks/register-a-webhook) * webhooks should be treated as notifications only. To verify the payment status, use the `payment_id` from the webhook and call the getPayment API: [https://docs.tabby.ai/api-reference/payments/retrieve-a-payment](https://docs.tabby.ai/api-reference/payments/retrieve-a-payment) ## What should I do if Payments remain in status NEW in Tabby Merchant Dashboard? Associated articles: * [Payment Statuses](https://docs.tabby.ai/pay-in-4-custom-integration/payment-statuses); * [Payment Processing](https://docs.tabby.ai/pay-in-4-custom-integration/payment-processing). ### Self-Hosted Platforms Self-Hosted Platforms are: **WooCommerce, Shopify, Magento 2, OpenCart**. These payments are likely lost (not linked to any created order). Merchants should select one of the options: * either cancel them (i.e., refund the customer): [https://docs.tabby.ai/api-reference/payments/close-a-payment](https://docs.tabby.ai/api-reference/payments/close-a-payment) * or manually create an order for such payments, as they are authorized by Tabby, and capture payments: [https://docs.tabby.ai/api-reference/payments/capture-a-payment](https://docs.tabby.ai/api-reference/payments/capture-a-payment) * or just capture them if the orders exist. Additionally, merchants should update the plugin to the latest version (following Tabby documentation) and check the plugin settings to prevent similar cases in the future. ### Zid and Salla Platforms By default, everything is expected to be working successfully when live keys are saved. If you suspect an issue or have lost orders, kindly re-save the keys and contact your platform's support team to verify the situation or register webhooks to prevent such issues. ### Custom (Direct API) Integrations Merchants may have immediate or delayed captures, meaning `NEW` statuses may simply be waiting for merchant action. However, in many cases, these payments are also lost. Merchants should select one of the options: * either cancel them (i.e., refund the customer): [https://docs.tabby.ai/api-reference/payments/close-a-payment](https://docs.tabby.ai/api-reference/payments/close-a-payment) * or manually create an order for such payments, as they are authorized by Tabby, and capture payments: [https://docs.tabby.ai/api-reference/payments/capture-a-payment](https://docs.tabby.ai/api-reference/payments/capture-a-payment) * or just capture them if the orders exist. Merchants should also check payment logs and investigate integration issues to determine why the payment wasn't acknowledged or captured in time (following Tabby documentation). The Integrations team can assist with investigating API calls and webhooks - kindly contact them via the integrations thread or an assigned business manager. ### Tabby Card and Payment Links These are always expected to be Auto-Captured. If they are not - kindly provide your store name with Tabby to Tabby Partner Support `partner@tabby.ai` / `partner@tabby.sa` or to your assigned business manager. ## What is Eligibility check? Associated articles: * [Background Pre-scoring Check](https://docs.tabby.ai/pay-in-4-custom-integration/checkout-flow#background-pre-scoring-check); * [Integration overview](https://docs.tabby.ai/pay-in-4-custom-integration/quick-start). ### How can I know that a customer is not eligible to use Tabby? In the Tabby Session creation response, you can receive a status like this if a customer is not eligible to use Tabby: ``` "status": "rejected", "configuration"."products"."installments"."rejection_reason": "not_available" ``` You need to hide Tabby payment option or show it with the General Rejection message or specific reason message depending on the `"rejection_reason"` value: | Reason | English | Arabic | | - | - | - | | General Rejection (`not_available`) | Sorry, Tabby is unable to approve this purchase. Please use an alternative payment method for your order. | نأسف، تابي غير قادرة على الموافقة على هذه العملية. الرجاء استخدام طريقة دفع أخرى. | | `order_amount_too_high` | This purchase is above your current spending limit with Tabby, try a smaller cart or use another payment method | قيمة الطلب تفوق الحد الأقصى المسموح به حاليًا مع تابي. يُرجى تخفيض قيمة السلة أو استخدام وسيلة دفع أخرى. | | `order_amount_too_low` | The purchase amount is below the minimum amount required to use Tabby, try adding more items or use another payment method | قيمة الطلب أقل من الحد الأدنى المطلوب لاستخدام خدمة تابي. يُرجى زيادة قيمة الطلب أو استخدام وسيلة دفع أخرى. | For detailed implementation guide check here: [Background Pre-scoring Check](/pay-in-4-custom-integration/checkout-flow#background-pre-scoring-check) ### What is the difference between eligibility check and checkout session creation? Tabby's Checkout API serves two distinct purposes. Here's a side by side comparison: | Aspect | Eligibility Check | Checkout Session Creation | | - | - | - | | **Purpose** | Determine if customer can use Tabby | Create payment session for actual checkout | | **Timing** | Before showing Tabby payment method | When customer clicks "Place order" | | **Payload** | Minimal (amount, currency, buyer) | Complete (all order details, items, shipping, merchant URLs) | | **Response** | Status (`created` or `rejected`) + rejection reason | Status + `web_url` for redirect + `payment.id` | | **Next Step** | Show or hide / mark unavailable Tabby payment method based on the response | Redirect to Tabby Hosted Payment Page | ### Example of a minimal payload for eligibility check ```json theme={"dark"} { "payment": { "amount": "340.00", "currency": "SAR", "buyer": { "email": "otp.success@tabby.ai", "phone": "+966500000001" } }, "merchant_code": "your_merchant_code" } ``` **Key principle**: Use the same endpoint (`POST /api/v2/checkout`) but with different payload sizes for different purposes. The minimal payload is accepted **for the background pre-scoring call only** — the session you create when the customer clicks "Place order" must carry the [full payload](/api-reference/checkout/session-payload-model) with all required fields. ### When should I perform eligibility check? Perform the eligibility check **once per checkout session** before displaying payment methods to customer, when you have the basic order details (amount, currency, buyer.phone, buyer.email). **Best practices:** * **Payload**: Include as much data as possible (not just minimal payload) for better approval rates and conversion * **Timing**: Check eligibility when customer enters checkout page with all main data available * **Error handling**: If API times out or returns error, default to showing Tabby option (fail-safe approach) - never block checkout * **Two separate calls**: Eligibility check (minimal/recommended data) ≠ Session creation (complete order details when customer clicks "Place order") ## How should I validate checkout session response? Always validate the checkout session response before redirecting the customer. Here are the critical validation steps: **Critical Validation Steps**: 1. **Check `status` field** - must be `"created"`. If `"rejected"`, show rejection message and don't redirect; 2. **Validate `web_url` presence** - never redirect without it. Show rejection message if missing; 3. **Store `payment.id`** - required for subsequent capture/refund operations. For complete validation logic and edge cases check here: [Checkout Flow - Response Validation](/pay-in-4-custom-integration/checkout-flow#response-and-validation). ## Is it possible to know the rejection reason for payments? Unfortunately, due to compliance and regulatory restrictions, Tabby is not permitted to disclose the specific rejection reasons to merchants. Customers, however, typically see a rejection message on the Tabby-hosted payment page at the time of the transaction. If you suspect a technical issue that may be causing multiple or all transactions to be rejected - please, contact Tabby Partner Support at `partner@tabby.ai` (or `partner@tabby.sa` for KSA) or reach out to your assigned Business Manager. When doing so, kindly include: 1. your store name as registered with Tabby; 2. your integration details: * website URL or app name (for online integrations); * description of offline integration, if applicable. ## Are there any transaction limits set for my store from Tabby side? Tabby applies transaction limits based on a combination of factors, including your industry and associated risk levels. Additionally, customer-level limits are dynamically determined based on their historical transaction behaviour with your store and with other merchants using Tabby. For this reason, we recommend **not setting hardcoded limits** for Tabby transactions on your end, as these may conflict with Tabby's dynamic risk and approval models. ## What should I do if a customer is not redirected back to the website? When customers finish the Tabby session, they are redirected back to your site from the Tabby Payment Page via one of the three `merchant_urls`, with the `payment_id` after the separator, e.g. `https://your-store/success?payment_id=string`: ``` "merchant_urls": { "success": "https://your-store/success", "cancel": "https://your-store/cancel", "failure": "https://your-store/failure" } ``` The `merchant_urls` should be included in a [Session Creation request](https://docs.tabby.ai/api-reference/checkout/create-a-session#body-merchant-urls) sent to Tabby from your side. ## What should I do if I do not receive fields in a response from Tabby API? If some fields do not return in the response: * check the endpoint used for the request; * check the fields format in the request - they should match those specified in the [API Reference Documentation](https://docs.tabby.ai/api-reference/overview); * check responses in the [API Reference Documentation](https://docs.tabby.ai/api-reference/overview) and that these fields are expected to be returned. ## Where can I see if Tabby is working normally? Status Page Recommendations. Associated article: [Technical Requirements -> Status Page](https://docs.tabby.ai/introduction/technical-requirements#status-page). Recommendations for status page maintenance and incidents: * Tabby sends alerts through [https://www.tabby-status.com/](https://www.tabby-status.com/). Please, subscribe using your preferred channel for updates. * Tabby posts alerts manually in real-time within 5-10 minutes of identifying and confirming an issue. The alerts provide details about the affected systems. * If Checkout or Payments APIs are affected for all countries, we recommend disabling Tabby until the next message confirms the resolution. * If only one country is affected, you can disable Tabby for that specific country. * If non-real-time systems are affected, no action is required (incident details will still be posted). * If you see a Scheduled Technical Maintenance notification, you can plan disabling Tabby for that duration of the maintenance window or mute monitors if the expected timeframe is less than 10 minutes, as it will recover automatically. * If you suspect an issue with any flow, you can contact Tabby Partner Support `partner@tabby.ai` / `partner@tabby.sa` for more details. However, if you observe that Tabby APIs are not working (e.g., consistently returning 5xx or 4xx errors), please contact the Tabby Integrations Team via the integration thread or via your assigned business manager immediately to investigate and resolve the issue. In the meantime, disable Tabby until the issue is resolved. ## How does Tabby work for shoppers? Shoppers choose Tabby at checkout — no account is needed beforehand. If they don't have one yet, it is created automatically during their first purchase. At checkout, the shopper enters their mobile number, verifies it with an SMS code, then reviews and confirms their payment plan before the order goes through. ## Who is eligible to use Tabby? To be eligible to use Tabby, a shopper must: * be at least 18 years of age; * have a valid National ID; * have a valid debit or credit card to cover payments. Each purchase is also approved individually — see [What is Eligibility check?](/introduction/faq#what-is-eligibility-check). ## If a refund is requested, will the fees or Murabaha profit be refunded too? Yes: * a **full refund** from the merchant returns the purchase amount plus any fee or Murabaha profit charged on that plan; * a **partial refund** returns that fee or profit proportionally. Murabaha profit applies in KSA only. How a refund changes the customer's downpayment and remaining instalments: [Payment Processing → How a Refund Affects the Customer's Instalments](/pay-in-4-custom-integration/payment-processing#how-a-refund-affects-the-customers-instalments). ## General information For additional support and information, you may also find helpful resources here: [Tabby Support Center](https://support.tabby.ai/l/en). # Quick Start Source: https://docs.tabby.sa/introduction/quick-start Choose your integration type below. Use pre-built plugins for popular platforms or implement custom integration via Tabby API. ## E-Commerce Platforms > **[Salla](/e-commerce-platforms/salla)** > >  > **[Shopify](/e-commerce-platforms/shopify)** > >  > **[Zid](/e-commerce-platforms/zid)** > >  > **[WooCommerce](/e-commerce-platforms/woocommerce)** > >  > **[Magento 2](/e-commerce-platforms/magento-2)** > >  > **[OpenCart](/e-commerce-platforms/opencart)** > >  > **[Matjrah](/e-commerce-platforms/matjrah)** > >  > **[Odoo](/e-commerce-platforms/odoo/odoo-plugin-installation)** > >  > **[Salesforce](/e-commerce-platforms/salesforce)** > >  ## Custom Integrations > **[Online Custom Integration](/pay-in-4-custom-integration/quick-start)** > >  > **[Offline: POS Integration](/offline-payment-methods/pos-integration)** > >  > **[Offline: Custom Payment Links](/offline-payment-methods/custom-payment-links)** > >  > **[Testing Guidelines](/testing-guidelines/testing-credentials)** > >  # Technical Requirements Source: https://docs.tabby.sa/introduction/technical-requirements This page gives you the common knowledge about Tabby operating. ## Status Page [Tabby Status Page](https://www.tabby-status.com/) offers live status, maintenance alerts and incident history reports for Tabby Services. Please subscribe to our Status Page to stay informed about all changes and maintenance works and be able to manage your sales according to it. ## Security Protocol **TLS** is an industry-standard protocol for encrypting network communications and establishing the identity of websites over the Internet. Tabby API supports TLS version 1.2 and higher. Additionally, we rely on **HTTPS** to ensure all data is transmitted securely. Strongly restricted cipher suites for compliance with the Payment Card Industry Data Security Standard. Enhances payment card data security: ``` TLSv1.3: TLS_AES_128_GCM_SHA256 TLS_AES_256_GCM_SHA384 TLS_CHACHA20_POLY1305_SHA256 TLSv1.2: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 ``` ## Allowlist Tabby uses several IP addresses when sending webhook requests and new IPs may be utilized as our systems scale and new resources are brought online. Please allow this list of IP addresses to prevent webhook calls from failing: ``` 34.166.36.90 34.166.35.211 34.166.34.222 34.166.37.207 34.93.76.191 34.166.128.182 34.166.170.3 34.166.249.7 ``` ## Data Format Request and response data are formatted as **JSON**. The following data formats are used across all Tabby APIs: ### Currency We use the [ISO 4217](https://en.wikipedia.org/wiki/ISO%5F4217) standard for defining currencies. ```JSON theme={"dark"} { "currency": "AED" } ``` ### Amount Amounts are **decimal strings in major currency units** — e.g. one hundred dirhams is `"100.00"`, not `10000`. Currency codes follow the [ISO 4217](https://en.wikipedia.org/wiki/ISO%5F4217) standard. Tabby allows to send: * up to **2 decimals** for *AED* and *SAR*. ```JSON theme={"dark"} { "amount": "100.00" } ``` **Keep the totals consistent.** `payment.amount` must equal the sum of your order lines: `Σ (item.unit_price × quantity − item.discount_amount) + tax_amount + shipping_amount − order.discount_amount`. The classic mistake is rounding twice — compute each line, round it **once** to the currency's decimals, then sum the already-rounded values to produce `amount`. Deriving `amount` independently from the cart (or rounding the grand total differently than the lines) produces off-by-a-fils mismatches that fail validation and QA. ### Phone The following mobile phone formats are accepted, using the UAE +971 mask and phone number as an example: ```JSON theme={"dark"} { "phone": "+971500000001" // OR "971500000001", "500000001", "0500000001" } ``` ### Merchant Code Merchant code is a unique store identifier under one brand and should be sent as a string value. Usually `merchant_code` represents a merchant country or a specific store within the country. ### Dates The [ISO 8601](https://en.wikipedia.org/wiki/ISO%5F8601) standard with combined Date and Time in UTC for all API dates. The exceptions to this are `dob` fields where we accept values in the **YYYY-MM-DD** format. ```JSON theme={"dark"} { "registered_since": "2019-08-24T14:15:22Z", "dob": "2019-08-24" } ``` ### Locale Operating in the GCC region, Tabby supports the English and Arabic languages and refers to the [RFC 1766](https://en.wikipedia.org/wiki/IETF%5Flanguage%5Ftag#List%5Fof%5Fcommon%5Fprimary%5Flanguage%5Fsubtags) standard. ```JSON theme={"dark"} { "lang": "en" } ``` ### String Length Validation We are processing a maximum of 255 symbols in the **"string"** field. ### Allowed characters in redirect URLs ("success", "cancel", "failure") 1. Latin letters (a-z, A-Z) 2. Arabic letters (ء-ي) 3. Digits (0-9) 4. Special characters - \ | / : ;., + \{}? & @ = # % ## Rate limit Tabby applies rate limits to keep the API stable for all merchants. Limits apply to Create Session requests (`POST /api/v2/checkout`) and are counted per API key. A request over the limit is rejected with HTTP status `429 Too Many Requests` and is not processed, so it is safe to retry. | API key | Create Session limit | | - | - | | Live | 100 requests per 10 seconds | | Test | 10 requests per 10 seconds | Treat these values as upper bounds, not as guaranteed throughput: * Requests are counted over a rolling window. A short burst can be throttled even if your average rate is below the limit. * Tabby may adjust limits to protect the platform. Your integration should handle `429` at any volume. ### Handling 429 responses * Retry with exponential backoff and jitter, for example after 1, 2 and 4 seconds plus a random delay. Do not retry in a tight loop and do not rely on the `Retry-After` header. * Keep checkout working if a session can't be created: hide Tabby or show it as temporarily unavailable, and let the customer pay with another method. * Avoid duplicate sessions for the same checkout, for example on every page render or field update. ### Higher limits If you expect a traffic peak, such as a sale campaign, contact your Tabby account manager in advance to discuss higher limits. > [!WARNING] > > Do not run load or stress tests against the Production API. Exclude Tabby from checkout when you load test your store. Keys and IP addresses that generate such traffic may be blocked automatically by the firewall, and payments made with them may be restricted. ## Authentication To authenticate with Tabby you will use your API credentials and HTTP basic auth. These credentials consist of two elements: * **Secret Key**: Associated with your merchant account, this key is used to authorize requests to Tabby’s Checkout and Payments APIs. Send it as a **Bearer token** in the `Authorization` header with every request: ```bash theme={"dark"} curl https://api.tabby.ai/api/v2/checkout \ -H "Authorization: Bearer sk_test_..." \ -H "Content-Type: application/json" \ -d '{ ... }' ``` * **Public Key**: Associated with your merchant account, this key is used for promo snippets, plans, and customization support. Live API keys can be obtained: * from Tabby Merchant Dashboard for **Self-Hosted plugin** integration; * by contacting Tabby account manager after QA testing carried out by Tabby team is completed for **Custom API** integration. If the `Authorization` header is missing, doesn't use the `Bearer` scheme, or carries a wrong key, Tabby responds with `401 Not authorized` — see [API Error Codes](/api-reference/response-codes). ## Errors Tabby APIs use HTTP status codes alongside error objects to handle errors. When an API call fails, Tabby responds with a `4xx`/`5xx` status code and a JSON body containing `status: "error"`, an `errorType` value, and either a single `error` message or an `errors` array for field-level validation failures. The `error` value is a human-readable English message to aid in debugging. It is not meant to be displayable to end-users, nor to be machine-readable — log it to assist in debugging, but never parse it. Branch your handling on the HTTP status code and `errorType` instead. The full list of errors per endpoint, with example bodies and remediation, lives on the [API Error Codes](/api-reference/response-codes) page. ## Supported Browsers & Devices > [!TIP] > > We support all common-spread desktop and mobile browsers and mobile devices. As part of our development process, we test among all major browsers and across different versions of browsers. However, we do not support browsers that no longer receive security updates. Please, contact us if you have an issue with Tabby Checkout on a specific browser so we can improve its support. ### Browsers & Versions Chrome, Firefox, Safari and Microsoft Edge are supported on all platforms for three years from the version release. We also test across different mobile platforms: iOS 12 and above and Android 7 and above. # What is Tabby? Source: https://docs.tabby.sa/introduction/what-is-tabby Tabby is MENA's biggest Buy Now Pay Later, operating in KSA and UAE. Tabby lets merchants grow their business by offering their shoppers flexible payments, and is constantly growing its product offering. * Pay in Installments - Split your purchase into several payments * Online - easily through nearly any e-commerce platform * In-Store - with the Tabby Card, through paylinks or with a QR code * Pay next month - Pay for groceries, food, rides at the end of the month  # Drive more sales with Tabby Source: https://docs.tabby.sa/marketing/toolkit Customers who know you offer Tabby spend more and come back more often. The Marketing Toolkit has everything you need to spread the word: on your site, in your store and everywhere in between. Approved messaging, brand guidance, logos and ready-to-use assets for your website, app, email, social and store, now in one deck. [Explore the marketing toolkit](https://www.figma.com/deck/kNKIJEr2U4DsEcQQWOBw73/Tabby-%E2%80%94-Merchant-guidelines_EN?node-id=7-1460\&scaling=min-zoom\&content-scaling=fixed\&page-id=0%3A1) # Custom Payment Links Integration Source: https://docs.tabby.sa/offline-payment-methods/custom-payment-links ## Integration Overview This integration allows customers to pay using Tabby by receiving a payment link via SMS/Push. The flow involves: 1. Creating a payment session 2. Sending payment link to customer 3. Customer completes payment on their device 4. Merchant receiving payment confirmation > [!NOTE] > > To implement sending payment link via SMS/Push from Tabby side to a customer a POST request to an API endpoint `/api/v2/checkout/{id of session}/send_hpp_link` should be triggered from your side first. Instructions on how and when to implement it can be found below. ## Quick Reference | **API Endpoint** | **Purpose** | **Method** | | - | - | - | | `/api/v2/checkout` | Create session and payment | POST | | `/api/v2/checkout/{id of session}/send_hpp_link` | Send payment link via SMS/Push | POST | | `/api/v2/payments/{payment.id}` | Retrieve payment status | GET | | `/api/v2/checkout/{id of session}/cancel` | Cancel session | POST | | **Key Status Codes** | **Description** | | - | - | | `CREATED` | Payment initiated, waiting for completion | | `AUTHORIZED` | Payment approved, not yet captured | | `CLOSED` with "captures" object | Payment approved and captured successfully | | `REJECTED` | Payment declined | | `EXPIRED` | Session/payment expired or cancelled | ## Steps to Integrate Tabby using Custom Payment Links *** *** *** *** *** *** *** *** ## Integration Flow ```mermaid theme={"dark"} sequenceDiagram autonumber participant Customer participant POS as POS Terminal participant Provider as POS Backend participant Checkout as Tabby Checkout participant TabbyAPI as Tabby API Customer ->>+ POS: Request to pay with Tabby POS ->>+ Provider: Initiate payment Provider ->>+ TabbyAPI: POST /api/v2/checkout