# Leat API Documentation > The REST API behind the Leat loyalty platform: contacts, credits, > rewards, giftcards, prepaid, promotions, orders and the webhooks that report > on them. Every page listed here is Markdown; drop the .md for the human > version. Requests are authenticated with a Personal Access Token — > `Authorization: Bearer {{ access_token }}` — and endpoints live > under `https://api.leat.com/api/v3/oauth/clients`. [Back to home](https://docs.leat.com/v3.md) # ChangeLog > **Note** > > Stay up to date for all developments concerning our API, highly recommended for integrating partners. Sign up [here](https://forms.piggy.eu/forms/41981043-a265-4fcc-88e1-a6c7979f62d3). Every change to the API lands here, newest first. That means new endpoints and fields, new webhook events, deprecations, and anything that changes how a call you already use behaves. ## October 2026 October 8, 2026 - The `attributes` filter on [List Contacts](https://docs.leat.com/v3/oauth/contacts.md#list-contacts) is now documented: `attributes[name][operator]=value` filters on a contact attribute by its name, with operators that depend on the attribute's type. A value without an operator means `eq`, and a Contact has to match every filter. - In [List Contacts](https://docs.leat.com/v3/oauth/contacts.md#list-contacts), `current_values.id` now holds the Contact's id, the same as `contact_id`. It used to hold the id of an internal search row. `added_at` and `last_updated_at` now repeat the Contact's `created_at` and `updated_at`. All other keys and formats are unchanged, and `current_display_values` still repeats `current_values`. The response example now shows the real shape. October 7, 2026 - A boolean Custom Attribute without a value now reads `""` in `current_display_values`, the same as any other attribute without a value. It used to read `"False"` (or its translation), which made an unknown value look like false. A stored true or false reads as before, and `current_values` returns `null` for an unknown boolean, as before. This applies to the single-Contact endpoints on [Contacts](https://docs.leat.com/v3/oauth/contacts.md), [Portal Sessions](https://docs.leat.com/v3/oauth/portal-sessions.md) and [Webhooks](https://docs.leat.com/v3/oauth/webhooks.md). [List Contacts](https://docs.leat.com/v3/oauth/contacts.md#list-contacts) repeats `current_values` in `current_display_values`, so there an unknown boolean reads `null`. October 5, 2026 - [Create Loyalty Token](https://docs.leat.com/v3/oauth/loyalty-tokens.md#create-loyalty-token) is now documented: it returns the full Loyalty Token link for a Shop, `unique_id` and either `credits` or a `unit_value` with its `unit_name`. - The worked example on [Loyalty Tokens](https://docs.leat.com/v3/oauth/loyalty-tokens.md) is corrected: the credits example now shows its own hash, the parameters are joined with a single `&`, and the final URL is `/l?` followed by the query string including `hash`. Values must be URL-encoded before hashing, exactly as they appear in the link. - A Loyalty Token link can be claimed once, by a Contact signed in to the Contacts Portal, and its `unique_id` must be unique within your loyalty program. The `timestamp` is not used to expire the link. October 2, 2026 - [Reverse Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#reverse-reward-reception) now also gives back what the claim used up: the reversed reception no longer counts towards the Contact's limit on that Reward, and a physical Reward with a limited stock gets one back in stock. A digital Reward's code is not released. - [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception) on a digital Reward with no unused codes left now fails with `1018` (the Reward is out of stock), the same as a physical Reward at 0 stock. It used to fail with `1017`, the code for insufficient credits. - The limits on a Reward are described on [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception): the limit per contact counts over the Contact's whole lifetime, and stock is shared by all Contacts. October 1, 2026 - [Create Booking](https://docs.leat.com/v3/oauth/bookings.md#create-booking) now requires an `external_id`, and [Update Booking](https://docs.leat.com/v3/oauth/bookings.md#update-booking) ignores it, because it identifies the Booking in your system and does not change. The `type` of a Booking is now one of `EXPERIENCE`, `LOCATION`, `EQUIPMENT`, `RESTAURANT`, `HOTEL` or `VEHICLE`. ## September 2026 September 29, 2026 - [Bookings](https://docs.leat.com/v3/oauth/bookings.md) can now contain Line Items, each linked to a Product and with optional Sub Line Items, along with a `currency` and a `type`. A Product is found by its `external_identifier` and created when it does not exist yet. - [Update Booking](https://docs.leat.com/v3/oauth/bookings.md#update-booking) replaces the Line Items when `line_items` is given, matching them on `external_identifier`, and keeps them as they are when it is left out. An `external_identifier` that is already used by a Line Item of another Booking is rejected. - [Booking Products](https://docs.leat.com/v3/oauth/booking-products.md) and [Booking Categories](https://docs.leat.com/v3/oauth/booking-categories.md) can now be listed, created, found, updated, deleted and imported in batches, so the products that can be booked and their Categories can be set up upfront. September 25, 2026 - The `contact_updated` example on [Webhooks](https://docs.leat.com/v3/oauth/webhooks.md) no longer shows an `updated_attributes` array. It was never sent: the payload is the whole contact after the change, the same shape as `contact_created`, including `current_display_values`. Nothing changed about what is sent. - Retries are described as they work: a delivery gets five attempts in total, with waits of roughly 10, 100, 1,000 and 10,000 seconds between them, and is then marked failed. The 27-hour wait the pages mentioned is never reached. See [Retries](https://docs.leat.com/v3/oauth/webhooks.md#retries). - [Create Webhook Subscription](https://docs.leat.com/v3/oauth/webhooks.md#create-webhook-subscription) now says that an integration has one subscription per event type, and that creating another for the same event overwrites it. The `attributes` field on [Update Webhook Subscription](https://docs.leat.com/v3/oauth/webhooks.md#update-webhook-subscription) is stored but doesn't filter which contact updates are sent. September 12, 2026 - Fourteen webhook event types Leat has been delivering for some time are now documented, with their payloads: `booking_created`, `booking_updated`, `visit_created`, `contact_identifier_updated`, `contact_was_subscribed`, `contact_was_unsubscribed`, `tier_reached`, `tier_dropped`, `points_transaction_created`, `giftcard_created`, `giftcard_updated`, `voucher_expired`, `reward_redeemed` and `order_created`. Nothing changed about what is sent — see [Webhooks](https://docs.leat.com/v3/oauth/webhooks.md). - `tier_dropped` now delivers. The event type could be subscribed to before, but nothing was ever sent for it, so a subscription that has been sitting idle will start receiving calls when a contact moves down a tier. - The event list is grouped by what the event is about — contacts, loyalty, giftcards, vouchers, rewards, orders and visits — instead of the order the events happened to be added in. September 11, 2026 - **Webhooks V2** is here: one request now carries an array of events instead of a single one, so a campaign that issues 100,000 vouchers reaches your endpoint in about 1,000 requests rather than 100,000. The event payloads are unchanged. Pass `version: "V2"` to [Create Webhook Subscription](https://docs.leat.com/v3/oauth/webhooks.md#create-webhook-subscription) to subscribe, and read [Webhooks V2](https://docs.leat.com/v3/oauth/webhooks-v2.md) first — receivers must be idempotent on `events[].event_uuid`. - V2 is becoming the standard for every Leat webhook and we advise building on it. [Webhooks V1](https://docs.leat.com/v3/oauth/webhooks-v1.md) stays supported, remains the default when no `version` is passed, and will not change on a live subscription; a retirement date will be announced well in advance. - Every page of this documentation is now also published as Markdown, so an agent can read the reference directly. Add `.md` to any page URL, or start from `llms.txt`. See [AI and agents](https://docs.leat.com/v3/ai-and-agents.md). ## April 2026 April 15, 2026 - Batch imports of [Products](https://docs.leat.com/v3/oauth/products.md) and [Categories](https://docs.leat.com/v3/oauth/categories.md) now accept up to 100 entities per request, with a maximum payload of 1MB. ## November 2025 November 21, 2025 - Rewards can now be updated over API. ## October 2025 October 16, 2025 - A Voucher can now be validated before it is redeemed, without locking it. ## September 2025 September 5, 2025 - [Order Returns](https://docs.leat.com/v3/oauth/order-returns.md) are now available, mirroring the create and process steps of the Orders API. ## August 2025 August 26, 2025 - Orders can now be simulated. The response returns the Order Actions that would be triggered, without creating anything. ## July 2025 July 21, 2025 - Existing Orders can now be updated over API. - [Products](https://docs.leat.com/v3/oauth/products.md) and [Categories](https://docs.leat.com/v3/oauth/categories.md) can now be found by their external identifier, created with find or create, and imported in batches. ## June 2025 June 16, 2025 - The [Referrals](https://docs.leat.com/v3/oauth/referrals.md) API now exposes the settings of the referral program. - A redeemed [Collectable Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md) can now be reversed. June 3, 2025 - The [Orders](https://docs.leat.com/v3/oauth/orders.md) API now separates creating an Order from processing it. Use **Create Order** followed by **Process Order** when payment is confirmed later, or **Create And Process Order** when both happen at once. ## May 2025 May 26, 2025 - [Products](https://docs.leat.com/v3/oauth/products.md) and [Categories](https://docs.leat.com/v3/oauth/categories.md) are now available over API. Linking Products to Order line items enables Product filtering on Promotions and Rewards. ## March 2025 March 26, 2025 - Contacts can now be found by any of their identifying attributes with a single [Find Contact](https://docs.leat.com/v3/oauth/contacts.md#find-contact) call. ## February 2025 February 25, 2025 - Two Contacts can now be merged over API, moving credits, Vouchers and transactions onto the surviving Contact. ## December 2024 December 18, 2024 - The [Orders](https://docs.leat.com/v3/oauth/orders.md) API is now live. Orders replicate the orders from your own system, and are the basis for Loyalty, Marketing, Data and Automation features. ## November 2024 November 14, 2024 - [Giftcards](https://docs.leat.com/v3/oauth/giftcards.md) can now be listed, and a single Giftcard can be retrieved by its UUID. ## September 2024 September 19, 2024 - Multi use Vouchers can now be redeemed over API. - A Voucher transaction can now be reversed. ## July 2024 July 15, 2024 - [Perks](https://docs.leat.com/v3/oauth/perks.md) can now be created, updated, retrieved and deleted over API. - Portal Sessions are now available for paying with and topping up Prepaid balance. - A [Contact Identifier](https://docs.leat.com/v3/oauth/contact-identifiers.md) can now be unlinked from a Contact, or deleted entirely. July 2, 2024 - An Account's [Brand Kit](https://docs.leat.com/v3/oauth/brand-kit.md) is now available over the OAuth API. ## June 2024 June 18, 2024 - [Prepaid Transactions](https://docs.leat.com/v3/oauth/prepaid-transactions.md) can now be listed and reversed. - A [Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md) can now be reversed. - A Contact's [Contact Identifiers](https://docs.leat.com/v3/oauth/contact-identifiers.md) can now be listed, and an existing identifier can be updated. ## May 2024 May 31, 2024 - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) and [Reward Receptions](https://docs.leat.com/v3/oauth/reward-receptions.md) can now be reversed. - Custom Attributes can now be updated over API. ## April 2024 April 26, 2024 - [Visits](https://docs.leat.com/v3/oauth/visits.md) and [Bookings](https://docs.leat.com/v3/oauth/bookings.md) are now available over API. April 15, 2024 - Portal Sessions are now available for redeeming and topping up [Giftcards](https://docs.leat.com/v3/oauth/giftcards.md). - A Giftcard can now be sent to a Contact by email. April 4, 2024 - A single [Reward](https://docs.leat.com/v3/oauth/rewards.md) or [Promotion](https://docs.leat.com/v3/oauth/promotions.md) can now be retrieved by its UUID. ## February 2024 February 15, 2024 - [Contact Session Tokens](https://docs.leat.com/v3/oauth/contact-session-tokens.md) are now available, so a Contact identified on one device can be picked up on another. ## December 2023 December 21, 2023 - [Portal Sessions](https://docs.leat.com/v3/oauth/portal-sessions.md) now cover Credit Receptions, Giftcards, Prepaid and Vouchers, so a full loyalty flow can run without building the screens yourself. ## October 2023 October 31, 2023 - [Custom Attributes](https://docs.leat.com/v3/oauth/custom-attributes.md) can now be listed and created over API. - [Referrals](https://docs.leat.com/v3/oauth/referrals.md) can now be listed over API. October 17, 2023 - [Vouchers](https://docs.leat.com/v3/oauth/vouchers.md) can now be created in batches. ## September 2023 September 28, 2023 - [Portal Sessions](https://docs.leat.com/v3/oauth/portal-sessions.md) are now live. A Portal Session hands a Contact a hosted screen for a loyalty action, which saves building that screen in your own integration. September 20, 2023 - A [Voucher](https://docs.leat.com/v3/oauth/vouchers.md) can now be locked while a transaction is in progress and released again if it does not complete. ## August 2023 August 7, 2023 - Contacts can now be created asynchronously with [Create Contact Async](https://docs.leat.com/v3/oauth/contacts.md#create-contact-async), which returns before the Contact is fully built. ## July 2023 July 14, 2023 - The Webhook subscription system is now live! The following events are now available for webhooks: contact\_created, contact\_updated, credit\_reception\_created, prepaid\_transaction\_created, voucher\_redeemed and form\_submission\_completed. ## June 2023 June 30, 2023 - Promotions and Voucher API calls are now available for both Register & OAuth API. June 28, 2023 - In the V3 OAuth API, it is now possible to retrieve an Account's Forms. - In the V3 OAuth API, it is now possible to retrieve an Account's Brand Kit. ## May 2023 May 9, 2023 - It is now possible to request a contact auth token. May 4, 2023 - Deleting Contacts is now possible over API. ## February 2023 February 3, 2023 - In the V3 OAuth API, it is now possible to create a loyalty token. ## October 2022 October 14, 2022 - In the V3 OAuth API, it is now possible to calculate credits before doing a credit reception. October 12, 2022 - In the V3 OAuth API, it is now possible to generate a login URL for the Custom App. ## August 2022 - In the V3 API, it is now possible to set loyalty transaction attributes on the 'Create Credit Reception' API call. ## July 2022 - 'Link Contact Identifier' API calls are now live for both OAuth & Register. - 'Claim Anonymous Contact' API calls are now live for both OAuth & Register. ## June 2022 - Units API calls are now available for the OAuth & Register API. - Automation API calls are now available for the OAuth API. - Contact Verification API calls are now available for the OAuth API. - The **Get Giftcard Transaction** API call is now available for both the Register API and the OAuth API. ## May 2022 - The **Subscription Types** and **Contact Subscriptions** API calls are now available for both the Register API and the OAuth API. ## April 2022 - The **List Loyalty Transactions** API calls are now available for both the [Register API](https://docs.leat.com/v3/registers/contacts#list-loyalty-transactions) and the [OAuth API](https://docs.leat.com/v3/oauth/loyalty-transactions.md). ## March 2022 - The new **V3 API** is now live! The new API contains some breaking changes – so make sure to carefully read the [migration guide](https://docs.leat.com/v3/tutorials/migrate-to-v3.md) – but also contains some new features and new API calls: - Prepaid transactions - Create custom Reward Attributes via API - Support for Units has been added - Support for Contact Identifiers has been added ## February 2022 - Anonymously registering customers is now possibly via API. ## November 2021 - The List Members API call is now available. ## October 2021 - Guides for employee loyalty programs are now live. - Claim Reward API calls can now also be conducted using a member ID instead of a loyalty card ID. ## September 2021 - The `Get Rewards` response has been expanded to include the 'external\_property' and 'media' properties. - The examples views are now part of larger tutorials which should give a better sense of the integration process. - OAuth guides and examples views as well as a Get Started guide have been added. - Also, important decisions that need to be made in the integration process are discussed more thoroughly. - All guides and examples are now available. - Example views for the Register API are now live. ## August 2021 - Added the `is_subscribed` field to the 'Update Marketing Recipient' API calls of the [OAuth API](https://docs.leat.com/v3/oauth/contact-subscriptions.md) and [Register API](https://docs.leat.com/v3/registers/contact-subscriptions). ## June 2021 - Added the [Correct Giftcard Transaction](https://docs.leat.com/v3/registers/giftcard-transactions#correct-giftcard-transactions) API call to the Register API. ## May 2021 - Example response in creation of reward receptions API calls have been updated. It now includes the new 'External Property'. Please note: the credits returned on reward receptions are now negative numbers. - Small change in the **Create Staged Credit Reception** call. You can now choose between setting credits or purchase amount. - Added the [Correct Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#correct-giftcard-transactions) API call to the OAuth API. ## April 2021 - Updated the Member and Loyalty Card calls to include Custom Attributes in the response. - Our first SDK for PHP has been released! Check it out [here](https://github.com/Piggy-Loyalty/piggy-php-sdk). - Removed all Sandbox references from the API Docs. Testing is to be done from the Production Environment from now on. Developers already testing on the Sandbox environment may continue to do so. - Added Staged Credit Reception calls for the Register API. ## March 2021 - Added Push to Endscreen call for the [Register API](https://docs.leat.com/v3/registers/giftcards). - Added Create Giftcard calls to the [Register API](https://docs.leat.com/v3/registers/giftcards#create-giftcard) and for the [OAuth API](https://docs.leat.com/v3/oauth/giftcards.md#create-giftcard). - Added both the [Update Member (Register API)](https://docs.leat.com/v3/registers/contacts#update-contact) calls and the [Update Member (OAuth API)](https://docs.leat.com/v3/oauth/contacts.md#update-contact) calls. - Information about [Custom Attributes](https://docs.leat.com/v3/oauth/custom-attributes.md) is now available. Relatively new entities, they concern both Loyalty and Marketing. - API calls for the Marketing system are now available. These API calls concern Marketing Recipients only, check them out for the [Register API](https://docs.leat.com/v3/registers/contact-subscriptions) and for the [OAuth API](https://docs.leat.com/v3/oauth/contact-subscriptions.md). - Added instructions on creating (most) entities in the Business Dashboard. The instructions have been added underneath each entity. - API calls for the Giftcards system are now available. These API calls concern the creation of giftcard transactions and retrieval of giftcard information. Check them out for the [Register API](https://docs.leat.com/v3/registers/giftcard-transactions) and for the [OAuth API](https://docs.leat.com/v3/oauth/giftcard-transactions.md). Source: https://docs.leat.com/changelog --- # Introduction > **Note** > > Stay up to date for all developments concerning our API. Sign up [here](https://forms.piggy.eu/forms/41981043-a265-4fcc-88e1-a6c7979f62d3). ### Welcome to Leat's API Reference. If you need help please send an email to partners@leat.com. We usually respond within a day. You can also connect with us through Slack, where you're in direct contact with our development team, who during business hours often respond in a matter of minutes. If you want to join us on Slack, please send us a request via email. Good luck! ### Get started We recommend starting out with reading a guide or taking a look at one of our blueprints. - [Error handling](https://docs.leat.com/v3/error-handling.md) — Proper error handling is crucial - [Authentication](https://docs.leat.com/v3/authentication.md) — The first step of any integration - [Tutorials: Point of Sale](https://docs.leat.com/v3/tutorials/pos.md) — Build an integration for your Point of Sale system Source: https://docs.leat.com/v3 --- # AI and agents Every page on this site is also published as Markdown, so a coding assistant can read the reference the same way you do. Nothing is written twice: the Markdown comes out of the same build as the page you are looking at, so it is never behind. ## Markdown for any page Add `.md` to any URL and you get that page as Markdown, endpoints, parameters, errors and example responses included: ``` https://docs.leat.com/v3/oauth/contacts.md ``` The **Copy as Markdown** button in the header does the same thing for the page you are on, which saves a round trip when you want to paste it into a chat. ## The whole reference Two files describe the site as a whole, following the [llms.txt convention](https://llmstxt.org). Point an agent at the index first and let it fetch the pages it needs: ``` https://docs.leat.com/llms.txt https://docs.leat.com/llms-full.txt ``` `llms.txt` lists every page with a one line description, which is the cheaper starting point. `llms-full.txt` is the entire reference in one file, for when you would rather hand over everything at once. ## Working with an assistant Most assistants take a URL straight from your message, so the quickest start is to give them the index and say what you are building: ``` Read https://docs.leat.com/llms.txt, then write the call that credits a contact after an order is paid. ``` Two things are worth checking afterwards, whichever assistant you use. Every request needs a Personal Access Token, which the model cannot know, so keep it out of your prompt and out of the generated code. And amounts are always in the currency's lowest denomination, which is a detail models regularly get wrong on their own. ## Related - [Authentication](https://docs.leat.com/v3/authentication.md) — The first step of any integration - [Tutorial: POS](https://docs.leat.com/v3/tutorials/pos.md) — Learn how to integrate Leat with your Point of Sale system Source: https://docs.leat.com/v3/ai-and-agents --- # Authentication ## Personal Access Tokens On our API routes, the use of Personal Access Tokens is now the preferred method of authentication. In the Business Dashboard, go to the app [Marketplace](https://business.leat.com/apps/integrations/apps/marketplace) and create a Private App. A Personal Access Tokens can then be created for that app. **Note:** the token will only be shown once, so make sure to copy it down. Authentication is done with the following header: `Authorization: Bearer {{ access_token }}` ### Expiration Upon creation of the Personal Access Token, an expiration date of your choosing can be set. ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/authentication --- # Error handling Requests that result in an error always have the same response syntax: ```json { "message": "", "code": , "status_code": 400 } ``` Error responses have a status code 400 and are accompanied by a declarative message. Underneath each of the available API calls, we've listed the errors you may encounter in that specific API call. However, you can also find the full list below. Hopefully the messages themselves are sufficiently clear. If you encounter an error and the error message is unclear or missing, please contact us. **Possible errors** | Code | Message | | --- | --- | | `7002` | Account inactive. | | `55018` | Loyalty Program not active. | | `1102` | Shop not linked to a Loyalty Program. | | `1103` | Loyalty Program not found. | | `1003` | Invalid input. | | `55012` | Member not found. | | `55031` | Contact not found. | | `60016` | Contact not anonymous. | | `60005` | Contact already exists. | | `55007` | Loyalty card not found. | | `55032` | User app not found. | | `1013` | Card not registered. | | `60001` | Credit reception creating in progress. | | `1015` | Maximum amount of credits reached. | | `1048` | This card was not found | | `6003` | Reward not found. | | `60007` | Unit not found. | | `55016` | The card is already linked to a member. | | `55046` | No default unit found. | | `55017` | Member already exists. | | `1017` | Onvoldoende punten | Insufficient credits. | | `1027` | POS Transaction not found. | | `6001` | The POS transaction is already linked to a credit reception. | | `1019` | You have reached your claim limit on this reward. | | `1018` | The stock on this reward is empty. | | `6007` | The linked template does not contain the right replacement variable. | | `1041` | Staged Credit Reception not found. | | `1034` | Staged credit reception is already redeemed. | | `1008` | Shop not found. | | `7011` | User has no access to this account. | | `5001` | Giftcard not found. | | `7010` | That giftcard is not valid. | | `5003` | Het bedrag op de kaart is niet toereikend. | Insufficient amount on giftcard. | | `5007` | Deze giftcard is niet gekoppeld aan een programma. | Giftcard not linked to Giftcard Program. | | `7015` | Shop does not have access to Giftcard Program. | | `5006` | Deze giftcard kan niet opgewaardeerd worden. | This giftcard cannot be incremented. | | `1114` | Only non re-incrementable giftcards can be used in the OAuthRoutes Clients API. | | `5008` | Maximale limiet op kaart bereikt. | Max amount will be exceeded. | | `5002` | Het bedrag op de kaart is verlopen. | This giftcard has expired. | | `55004` | Marketing program not found. | | `55020` | Marketing recipient not found. | | `55005` | Marketing program customer already exists. | | `55021` | Only Digital giftcards can by created using the public API. | | `1028` | Credit reception not found. | | `55024` | This credit reception was not created by this shop. | | `1010` | Kaart is niet geactiveerd. | Card is not activated. | | `55025` | This is not the giftcards last transaction. | | `60003` | Contact identifier not found. | | `60004` | Contact identifier inactive. | | `60006` | Contact identifier already exists. | | `60014` | Contact identifier is already linked. | | `60011` | Contact identifier is not linked to a contact. | | `60009` | Attribute already exists. | | `55030` | Subscription type not found. | | `13010` | Passcode not found. | | `55045` | Unit already exists. | | `60013` | Model not found. | | `60018` | Please submit credits when using v1 | | `60019` | Please submit units when using v2 | | `60000` | Usage limit reached. | ## Related - [Tutorial: POS](https://docs.leat.com/v3/tutorials/pos.md) — Learn how to integrate Leat with your Point of Sale system - [Authentication](https://docs.leat.com/v3/authentication.md) — The first step of any integration Source: https://docs.leat.com/v3/error-handling --- # Automations Automations are a key feature that help our clients automate a variety of tasks. With Automations, you can send out marketing emails automatically, set up loyalty programs, issue credits, and send digital gift cards. These Automations are driven by specific events and are designed to work with individual contacts. You can also start Automations manually or through our API for a specific contact. When using the API, you have the option to send extra data along with the Automation Run for use in that run. ## List Automations Get an overview of all created Automations for an account. ```http GET https://api.leat.com/api/v3/oauth/clients/automations ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "event": "physical_reward_reception_created", "name": "Send Treasure", "status": "inactive", "created_at": "2022-05-10T12:47:38+00:00", "updated_at": "2022-05-10T12:47:38+00:00" }, { "event": "contact_updated", "name": "Send email on birthday", "status": "active", "created_at": "2022-05-11T08:09:37+00:00", "updated_at": "2022-05-11T08:09:37+00:00" }, { "event": "credit_reception_created", "name": "Send mail on first credit reception", "status": "active", "created_at": "2022-05-17T10:28:11+00:00", "updated_at": "2022-05-17T10:28:11+00:00" }, ], "meta": [] } ``` ## Create Automation Run This method triggers an Automation. Please note that only the Automation with the 'contact\_updated' event can subsequently be used to create a run. This means that the `event` of the Automation that you provide should have a be of type 'contact\_updated'. You can add an array at `data`, which can then be used in the Automation. ```http POST https://api.leat.com/api/v3/oauth/clients/automations/runs ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_uuid` *string, required* — Contact for whom the Automation is to be triggered. - `automation_uuid` *string, required* — Automation to be triggered. - `data` *array, optional* — Data to be used in the Automation. **Response Example** ```json { "data": [], "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `60013` | Model not found. | ## Related - [Contact Identifiers](https://docs.leat.com/v3/oauth/contact-identifiers.md) — Contacts can also be found using Contact Identifiers - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/automations --- # Booking Categories Booking Categories are entities that group certain Booking Products, for instance the packages and set menus that can be booked. > **Warning** > > **Please note:** the *Bookings* API is now in beta. Some functionalities may not yet be available at that time. Please note that the API may be subject to change. ## List Categories Lists all Booking Categories. ```http GET https://api.leat.com/api/v3/oauth/clients/booking-product-categories ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `limit` *number, optional* — The maximum number of Categories to return - `page` *number, optional* — The page number to return **Response Example** ```json { "data": [ { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" } ], "meta": { "page": 1, "limit": 10, "viewing_from": 1, "viewing_to": 10, "last_page": 1, "total": 1 } } ``` ## Create Category Creates a new Booking Category and returns it. ```http POST https://api.leat.com/api/v3/oauth/clients/booking-product-categories ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Category **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80581` | A category already exists for this external identifier | **Response Example** ```json { "data": { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" }, "meta": [] } ``` ## Batch Create Categories Creates or updates a batch of Booking Categories, matched on their `external_identifier`. Accepts up to 250 Categories per request. The maximum allowed payload size is 1MB. ```http POST https://api.leat.com/api/v3/oauth/clients/booking-product-categories/batch ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `categories` *array of objects, required* — List of Categories - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Category **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | **Response Example** ```json { "data": { "status": "completed" }, "meta": [] } ``` ## Get Category Get a specific Booking Category by its external\_identifier. ```http GET https://api.leat.com/api/v3/oauth/clients/booking-product-categories/{external_identifier} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Possible errors** | Code | Message | | --- | --- | | `80580` | Product category not found | **Response Example** ```json { "data": { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" }, "meta": [] } ``` ## Find Category Use the `external_identifier` to find a Booking Category. ```http GET https://api.leat.com/api/v3/oauth/clients/booking-product-categories/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80580` | Product category not found | **Response Example** ```json { "data": { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" }, "meta": [] } ``` ## Find or Create Category Finds the Booking Category with the given `external_identifier`, or creates it, and returns it. The name is only used when the Category is created. ```http POST https://api.leat.com/api/v3/oauth/clients/booking-product-categories/find-or-create ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Category **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | **Response Example** ```json { "data": { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" }, "meta": [] } ``` ## Update Category Updates a Booking Category, addressed by its external\_identifier, and returns it. The external\_identifier itself cannot be changed. ```http PUT https://api.leat.com/api/v3/oauth/clients/booking-product-categories/{external_identifier} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `name` *string, optional* — The name of the Category **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80580` | Product category not found | **Response Example** ```json { "data": { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" }, "meta": [] } ``` ## Delete Category Deletes a Booking Category, addressed by its external\_identifier. Its external\_identifier can be used again afterwards. ```http DELETE https://api.leat.com/api/v3/oauth/clients/booking-product-categories/{external_identifier} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Possible errors** | Code | Message | | --- | --- | | `80580` | Product category not found | **Response Example** ```json [] ``` ## Related - [Bookings](https://docs.leat.com/v3/oauth/bookings.md) — Create or update Bookings - [Booking Products](https://docs.leat.com/v3/oauth/booking-products.md) — Create, view or update Booking Products Source: https://docs.leat.com/v3/oauth/booking-categories --- # Booking Products Booking Products are what can be booked (e.g. a bowling lane, a ticket or a set menu). They are used for the Line Items of Bookings. > **Warning** > > **Please note:** the *Bookings* API is now in beta. Some functionalities may not yet be available at that time. Please note that the API may be subject to change. ## List Products Lists all Booking Products. ```http GET https://api.leat.com/api/v3/oauth/clients/booking-products ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `limit` *number, optional* — The maximum number of Products to return (max 100) - `page` *number, optional* — The page number to return **Response Example** ```json { "data": [ { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "LANE-1H", "name": "Bowling lane, 1 hour", "description": "A lane for up to six players", "categories": [ { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" } ] } ], "meta": { "page": 1, "limit": 10, "viewing_from": 1, "viewing_to": 10, "last_page": 1, "total": 1 } } ``` ## Create Product Creates a new Booking Product and returns it. ```http POST https://api.leat.com/api/v3/oauth/clients/booking-products ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Product - `description` *string, optional* — The description of the Product - `categories` *array of objects, optional* — List of Categories that the Product belongs to, each referenced by its external_identifier - `external_identifier` *string, required* — External identifier of the Category **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80571` | A product already exists with this external identifier | | `80580` | Product category not found | **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "LANE-1H", "name": "Bowling lane, 1 hour", "description": "A lane for up to six players", "categories": [ { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" } ] }, "meta": [] } ``` ## Batch Create Products Creates or updates a batch of Booking Products, matched on their `external_identifier`. Accepts up to 100 Products per request. The maximum allowed payload size is 1MB. A Category that does not exist is skipped. ```http POST https://api.leat.com/api/v3/oauth/clients/booking-products/batch ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `products` *array of objects, required* — List of Products - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Product - `description` *string, optional* — The description of the Product - `categories` *array of objects, optional* — List of Categories that the Product belongs to - `external_identifier` *string, required* — External identifier of the Category **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | **Response Example** ```json { "data": { "status": "completed" }, "meta": [] } ``` ## Get Product Get a specific Booking Product by its external\_identifier. ```http GET https://api.leat.com/api/v3/oauth/clients/booking-products/{external_identifier} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Possible errors** | Code | Message | | --- | --- | | `80570` | Product not found | **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "LANE-1H", "name": "Bowling lane, 1 hour", "description": "A lane for up to six players", "categories": [ { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" } ] }, "meta": [] } ``` ## Find Product Use the `external_identifier` to find a Booking Product. ```http GET https://api.leat.com/api/v3/oauth/clients/booking-products/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80570` | Product not found | **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "LANE-1H", "name": "Bowling lane, 1 hour", "description": "A lane for up to six players", "categories": [ { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" } ] }, "meta": [] } ``` ## Find or Create Product Finds the Booking Product with the given `external_identifier`, or creates it, and returns it. The other attributes are only used when the Product is created. ```http POST https://api.leat.com/api/v3/oauth/clients/booking-products/find-or-create ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Product - `description` *string, optional* — The description of the Product - `categories` *array of objects, optional* — List of Categories that the Product belongs to, each referenced by its external_identifier - `external_identifier` *string, required* — External identifier of the Category **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80580` | Product category not found | **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "LANE-1H", "name": "Bowling lane, 1 hour", "description": "A lane for up to six players", "categories": [ { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" } ] }, "meta": [] } ``` ## Update Product Updates a Booking Product, addressed by its external\_identifier, and returns it. The external\_identifier itself cannot be changed. ```http PUT https://api.leat.com/api/v3/oauth/clients/booking-products/{external_identifier} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `name` *string, optional* — The name of the Product - `description` *string, optional* — The description of the Product - `categories` *array of objects, optional* — List of Categories that the Product belongs to. When given, it replaces the Categories the Product has - `external_identifier` *string, required* — External identifier of the Category **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80570` | Product not found | | `80580` | Product category not found | **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "LANE-1H", "name": "Bowling lane, 1 hour", "description": "A lane for up to six players", "categories": [ { "uuid": "0b1e8a2c-6d3f-4c51-9a7e-2f4d8b6c1a90", "external_identifier": "activity", "name": "Activity" } ] }, "meta": [] } ``` ## Delete Product Deletes a Booking Product, addressed by its external\_identifier. Its external\_identifier can be used again afterwards. ```http DELETE https://api.leat.com/api/v3/oauth/clients/booking-products/{external_identifier} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Possible errors** | Code | Message | | --- | --- | | `80570` | Product not found | **Response Example** ```json [] ``` ## Related - [Bookings](https://docs.leat.com/v3/oauth/bookings.md) — Create or update Bookings - [Booking Categories](https://docs.leat.com/v3/oauth/booking-categories.md) — Create, view or update Booking Categories Source: https://docs.leat.com/v3/oauth/booking-products --- # Bookings Bookings are replicas of the bookings made in your own reservation system. Leat holds enough of one to tie it to a Contact and a Shop, along with when the party is expected, how many people it covers and what was booked; your system stays the record the reservation is managed in. A Booking can contain Line Items, which link it to the Products that were booked (e.g. a bowling lane, a ticket or a set menu). The same Product is used for every Booking it is booked in. A Line Item can have Sub Line Items, such as the courses of a set menu. Packages are Products as well and can be recognised by their Categories. > **Warning** > > **Please note:** the *Bookings* API is now in beta. Some functionalities may not yet be available at that time. Please note that the API may be subject to change. ## Create Booking Creates a new Booking. Note that all monetary amounts – like prepaid\_amount – are to be given in the currency's lowest denomination (e.g. €34,50 -> 3450). When creating Bookings, the best practice is to create the Booking as completely as you can. This includes adding the Line Items of what was booked and linking them to Products. Some best practices to take into account: - `external_id: ` This is the main identifier from your system, usually a UUID or GUID or some other. It's imperative this is unique across all Bookings. - `line_items.external_identifier:` This is the unique identifier of this line item. Another line item of the same product should have their own unique identifier. When updating a Booking, send the same identifier again for a line item that is still part of it, so it is updated rather than replaced. - `line_items.total_amount:` The total amount of a line item ought to be the line item's price times the quantity, minus the discount amount. The total amount does not include the total amounts of any sub line items. They are counted separately. - `line_items.product:` It's highly recommended to link the product (if present). The external identifier counts as the main unique identifier of the product, which is of course static for the same product across multiple line items and bookings. If no product yet exists for the external identifier, one will be created. However, it's highly recommended to create the entire [product library](https://docs.leat.com/v3/oauth/booking-products.md#create-product), including [categories](https://docs.leat.com/v3/oauth/booking-categories.md#create-category), upon integration installation. - `line_items.sub_line_items:` Line items may include sub line items, like the courses of a set menu or the contents of a package. **Note:** the total amounts of the sub line items count separately from the line item's total amount. Example: a line item for a 'Set menu' with a total amount of 5900 (€59) may include a sub line item for 'Wine pairing' with a total amount of 2500 (€25), which would bring the total of the booking to 8400 (€84). The €25 from the sub line item is *not* included in the total amount on the parent line item. Sub line items are to be linked to products in the same way as their parent line items. If you have any questions about Bookings, please reach out to your partnership manager or contact us at dev@leat.com. ```http POST https://api.leat.com/api/v3/oauth/clients/bookings ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `shop_uuid` *string, required* — The UUID of the Shop the Booking was made at - `contact_uuid` *string, required* — The UUID of the Contact who made the booking - `external_id` *string, required* — An external identifier for the Booking, unique per Account. It cannot be changed afterwards - `source` *string, optional* — Source from where the Booking was created - `starts_at` *string, optional* — The start date and time of the Booking in ISO 8601 format - `ends_at` *string, optional* — The end date and time of the Booking in ISO 8601 format - `checked_in_at` *string, optional* — The date and time when the party checked into the booking in ISO 8601 format - `number_of_people` *number, optional* — The size of the party in the booking - `company_name` *string, optional* — The name of the company for which the booking was made - `status` *string, optional* — Status of the booking (e.g. created, in progress, canceled, completed) - `type` *string, optional* — Type of the Booking Possible values: - `EXPERIENCE` — An activity, e.g. bowling or karting - `LOCATION` — A venue or space, e.g. a meeting room - `EQUIPMENT` — Rentable equipment - `RESTAURANT` — A table at a restaurant - `HOTEL` — A hotel room or stay - `VEHICLE` — A rental vehicle - `prepaid_amount` *number, optional* — (in cents) The amount that was paid prior to the start of the booking - `currency` *string, optional* — Booking currency in ISO 4217 format; defaults to Account's default currency - `line_items` *array of objects, optional* — List of Products in this Booking - `external_identifier` *string, required* — A unique identifier for this Line Item - `name` *string, required* — Name of the Line Item - `quantity` *number, required* — Quantity of the Line Item, a whole number - `price` *number, required* — Price per unit of this Line Item - `total_amount` *number, required* — Total amount of this Line Item, in the currency's lowest denomination, after discounts - `discount_amount` *number, optional* — Discount amount of this Line Item, in the currency's lowest denomination. 0 when left out - `product` *object, optional* — Associated Product, optional but highly recommended. - `external_identifier` *string, required* — Unique identifier to identify the Product, Product will be created using external_identifier and line_item.name if not found - `name` *string, optional* — Name of the Product, used instead of line_item.name when the Product is created - `description` *string, optional* — Description of the Product, used when the Product is created - `sub_line_items` *array of objects, optional* — List of Sub Line Items of this Line Item - `external_identifier` *string, required* — A unique identifier for this Sub Line Item - `name` *string, required* — Name of the Sub Line Item - `quantity` *number, required* — Quantity of the Sub Line Item, a whole number - `price` *number, required* — Price per unit of this Sub Line Item - `total_amount` *number, required* — Total amount of this Sub Line Item, in the currency's lowest denomination, after discounts - `discount_amount` *number, optional* — Discount amount of this Sub Line Item, in the currency's lowest denomination. 0 when left out - `product` *object, optional* — Associated Product, optional but highly recommended. - `external_identifier` *string, required* — Unique identifier to identify the Product, Product will be created using external_identifier and sub_line_item.name if not found - `name` *string, optional* — Name of the Product, used instead of sub_line_item.name when the Product is created - `description` *string, optional* — Description of the Product, used when the Product is created **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `1008` | Shop not found. | | `80524` | A line item already exists for that external identifier | | `80525` | A sub line item already exists for that external identifier | | `80528` | Booking is already being created | **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "12345-abcdefg-6789-yuiop", "email": "spongebob@bikinibottom.sea" }, "external_id": "B-0012", "source": "Widget", "starts_at": "2024-05-01T18:00:00+00:00", "ends_at": "2024-05-01T21:00:00+00:00", "checked_in_at": "2024-05-01T18:04:46+00:00", "company_name": null, "number_of_people": 4, "status": "confirmed", "type": "EXPERIENCE", "prepaid_amount": 5000, "currency": "EUR", "line_items": [ { "uuid": "0f5d3c2a-7e41-4b9c-a6d2-8c1e5f9b3a70", "external_identifier": "B-0012-1", "name": "Bowling lane, 2 hours", "quantity": 1, "price": 9000, "total_amount": 9000, "discount_amount": 0, "product": { "external_identifier": "LANE-2H", "name": "Bowling lane, 2 hours", "categories": [ { "external_identifier": "ACTIVITIES", "name": "Activities" } ] }, "sub_line_items": [] }, { "uuid": "6b2e9d14-3a57-4c8f-b0e1-47d9c2a6f815", "external_identifier": "B-0012-2", "name": "Bowling & dinner package", "quantity": 4, "price": 3950, "total_amount": 14800, "discount_amount": 1000, "product": { "external_identifier": "PKG-BOWL-DINNER", "name": "Bowling & dinner package", "categories": [ { "external_identifier": "package", "name": "Package" } ] }, "sub_line_items": [ { "uuid": "a91c47e0-5d2b-4f36-8e7a-0b3f6d9c2e58", "external_identifier": "B-0012-2-1", "name": "Burger", "quantity": 4, "price": 0, "total_amount": 0, "discount_amount": 0, "product": { "external_identifier": "BURGER", "name": "Burger" } } ] } ], "created_at": "2024-04-25T13:35:09+00:00", "custom_attributes": [] }, "meta": [] } ``` ## Update Booking Updates the attributes of the Booking with the given `external_id`. The Shop and Contact cannot be updated. Neither can the `external_id`: sending a different one is rejected, while sending the one the Booking already has is fine. When `line_items` is given, it replaces the Line Items of the Booking. Line Items are matched on their `external_identifier`: the ones that are given again are updated, new ones are added and the ones that are left out are removed. Leave out `line_items` to keep the Line Items as they are. ```http PUT https://api.leat.com/api/v3/oauth/clients/bookings/{external_id} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `source` *string, optional* — Source from where the Booking was created - `starts_at` *string, optional* — The start date and time of the Booking in ISO 8601 format - `ends_at` *string, optional* — The end date and time of the Booking in ISO 8601 format - `checked_in_at` *string, optional* — The date and time when the party checked into the booking in ISO 8601 format - `number_of_people` *number, optional* — The size of the party in the booking - `company_name` *string, optional* — The name of the company for which the booking was made - `status` *string, optional* — Status of the booking (e.g. created, in progress, canceled, completed) - `type` *string, optional* — Type of the Booking Possible values: - `EXPERIENCE` — An activity, e.g. bowling or karting - `LOCATION` — A venue or space, e.g. a meeting room - `EQUIPMENT` — Rentable equipment - `RESTAURANT` — A table at a restaurant - `HOTEL` — A hotel room or stay - `VEHICLE` — A rental vehicle - `prepaid_amount` *number, optional* — (in cents) The amount that was paid prior to the start of the booking - `currency` *string, optional* — Booking currency in ISO 4217 format - `line_items` *array of objects, optional* — List of Products in this Booking - `external_identifier` *string, required* — A unique identifier for this Line Item - `name` *string, required* — Name of the Line Item - `quantity` *number, required* — Quantity of the Line Item, a whole number - `price` *number, required* — Price per unit of this Line Item - `total_amount` *number, required* — Total amount of this Line Item, in the currency's lowest denomination, after discounts - `discount_amount` *number, optional* — Discount amount of this Line Item, in the currency's lowest denomination. 0 when left out - `product` *object, optional* — Associated Product, optional but highly recommended. - `external_identifier` *string, required* — Unique identifier to identify the Product, Product will be created using external_identifier and line_item.name if not found - `name` *string, optional* — Name of the Product, used instead of line_item.name when the Product is created - `description` *string, optional* — Description of the Product, used when the Product is created - `sub_line_items` *array of objects, optional* — List of Sub Line Items of this Line Item - `external_identifier` *string, required* — A unique identifier for this Sub Line Item - `name` *string, required* — Name of the Sub Line Item - `quantity` *number, required* — Quantity of the Sub Line Item, a whole number - `price` *number, required* — Price per unit of this Sub Line Item - `total_amount` *number, required* — Total amount of this Sub Line Item, in the currency's lowest denomination, after discounts - `discount_amount` *number, optional* — Discount amount of this Sub Line Item, in the currency's lowest denomination. 0 when left out - `product` *object, optional* — Associated Product, optional but highly recommended. - `external_identifier` *string, required* — Unique identifier to identify the Product, Product will be created using external_identifier and sub_line_item.name if not found - `name` *string, optional* — Name of the Product, used instead of sub_line_item.name when the Product is created - `description` *string, optional* — Description of the Product, used when the Product is created **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80520` | Booking not found | | `80522` | The external id of a booking can’t be changed | | `80524` | A line item already exists for that external identifier | | `80525` | A sub line item already exists for that external identifier | **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "12345-abcdefg-6789-yuiop", "email": "spongebob@bikinibottom.sea" }, "external_id": "B-0012", "source": "Widget", "starts_at": "2024-05-01T18:00:00+00:00", "ends_at": "2024-05-01T21:00:00+00:00", "checked_in_at": "2024-05-01T18:04:46+00:00", "company_name": null, "number_of_people": 4, "status": "confirmed", "type": "EXPERIENCE", "prepaid_amount": 5000, "currency": "EUR", "line_items": [ { "uuid": "0f5d3c2a-7e41-4b9c-a6d2-8c1e5f9b3a70", "external_identifier": "B-0012-1", "name": "Bowling lane, 2 hours", "quantity": 1, "price": 9000, "total_amount": 9000, "discount_amount": 0, "product": { "external_identifier": "LANE-2H", "name": "Bowling lane, 2 hours", "categories": [ { "external_identifier": "ACTIVITIES", "name": "Activities" } ] }, "sub_line_items": [] }, { "uuid": "6b2e9d14-3a57-4c8f-b0e1-47d9c2a6f815", "external_identifier": "B-0012-2", "name": "Bowling & dinner package", "quantity": 4, "price": 3950, "total_amount": 14800, "discount_amount": 1000, "product": { "external_identifier": "PKG-BOWL-DINNER", "name": "Bowling & dinner package", "categories": [ { "external_identifier": "package", "name": "Package" } ] }, "sub_line_items": [ { "uuid": "a91c47e0-5d2b-4f36-8e7a-0b3f6d9c2e58", "external_identifier": "B-0012-2-1", "name": "Burger", "quantity": 4, "price": 0, "total_amount": 0, "discount_amount": 0, "product": { "external_identifier": "BURGER", "name": "Burger" } } ] } ], "created_at": "2024-04-25T13:35:09+00:00", "custom_attributes": [] }, "meta": [] } ``` ## Related - [Loyalty Tokens](https://docs.leat.com/v3/oauth/loyalty-tokens.md) — Create or Claim a Loyalty Token - [Reward Receptions](https://docs.leat.com/v3/oauth/reward-receptions.md) — It's what Contacts are saving up for: Reward Receptions Source: https://docs.leat.com/v3/oauth/bookings --- # Brand Kit A Brand kit expresses the identity of your company. A brand kit is a collection of designs and features that shape the personality and look of your company. Think about your logo, colors used and font. You can also add links to your social media accounts in emails and custom app through the Brand Kit. ## Get Brand Kit Retrieve the details of your account's Brand Kit with this call. It allows you to access all the branding elements associated with your account, including logos, color schemes, and font styles. ```http GET https://api.leat.com/api/v3/oauth/clients/brand-kit ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "small_logo_url": "https://images.google.com/photo_small", "large_logo_url": "https://images.google.com/photo_large", "primary_color": "#ad5959", "secondary_color": "#c8bcbc", "tertiary_color": "#a72f2f", "quarternary_color": "#317f88", "font_family": "Arial" }, "meta": [] } ``` ## Related - [Contact Identifiers](https://docs.leat.com/v3/oauth/contact-identifiers.md) — Contacts can also be found using Contact Identifiers - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/brand-kit --- # Categories Categories are entities that group certain Products. ## List Categories Retrieve all Categories. ```http GET https://api.leat.com/api/v3/oauth/clients/product-categories ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Coffee", } ], "meta": { "page": 1, "limit": 10, "viewing_from": 1, "viewing_to": 10, "last_page": 1, "total": 1 } } ``` ## Create Category Creates a new Category in the Leat system and returns the Category entity. ```http POST https://api.leat.com/api/v3/oauth/clients/product-categories ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Category **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Coffee", }, "meta": [] } ``` ## Batch Create Categories Creates a batch of Categories in the Leat system. Accepts up to 250 Categories per request. The maximum allowed payload size is 1MB. ```http POST https://api.leat.com/api/v3/oauth/clients/product-categories/batch ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `categories` *array of objects, required* — List of Categories - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Category **Response Example** ```json { "data": [ "status": "completed" ], "meta": [] } ``` ## Get Category Get a specific Category in the Leat system. ```http GET https://api.leat.com/api/v3/oauth/clients/product-categories/{uuid} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Coffee", }, "meta": [] } ``` ## Find Category Use the `external_identifier` to find a Category. ```http GET https://api.leat.com/api/v3/oauth/clients/product-categories/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Coffee", }, "meta": [] } ``` ## Find or Create Category Finds an existing Category or creates a new Category in the Leat system and returns the Category entity. ```http POST https://api.leat.com/api/v3/oauth/clients/product-categories/find-or-create ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, optional* — The name of the Category **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Coffee", }, "meta": [] } ``` ## Update Category Updates a Category in the Leat system and returns the updated Category entity. ```http PUT https://api.leat.com/api/v3/oauth/clients/product-categories/{uuid} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, optional* — A unique identifier from your system - `name` *string, optional* — The name of the Category **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Coffee", }, "meta": [] } ``` ## Delete Category Delete a specific Category in the Leat system. ```http DELETE https://api.leat.com/api/v3/oauth/clients/product-categories/{uuid} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": null, "meta": [] } ``` ## Related - [Orders](https://docs.leat.com/v3/oauth/orders.md) — Create or refund Orders - [Products](https://docs.leat.com/v3/oauth/products.md) — Create, view or update Products Source: https://docs.leat.com/v3/oauth/categories --- # Collectable Rewards Collectable Rewards represent Physical Rewards that have been claimed but are yet to be collected. These Rewards are stored at your Shop and await pickup by the Contact who claimed them. This section of the API deals with managing and tracking these Rewards. ## List Collectable Rewards Retrieve a collection of Physical Rewards that still need to be picked up at the Shop by the Contact that has claimed them remotely. ```http GET https://api.leat.com/api/v3/oauth/clients/collectable-rewards ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — Retrieve an overview of all uncollected rewards for the Contact using their UUID. **Response Example** ```json { "data": [ { "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "balance": 250 } }, "created_at": "2023-11-13T14:37:04+00:00", "uuid": "abc012-def789-u4k3l2-890xyz", "title": "Krabby Patty", "reward": { "id": 20002, "uuid": "539483d0-fab4-40db-add5-2ec65ef12901", "title": "Krabby Patty", "description": "Kelp Juice or Sea Water", "reward_type": "PHYSICAL", "media": { "type": "image", "value": "http://images.google.com/image/coffee/300x200.jpg" }, "required_credits": 2, "pre_redeemable": true, "expiration_duration": 6307200, "is_active": true, "cost_price": null, "stock": null, }, "expires_at": "2024-01-25T14:37:04+00:00", "has_been_collected": false } ], "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Redeem Collectable Reward To collect a Reward, the UUID of the Loyalty Transaction needs to be stated as query parameter inside the URI. It updates the existing Reward resource and sets the `has_been_collected`property to true. ```http PUT https://api.leat.com/api/v3/oauth/clients/collectable-rewards/collect/{{loyalty_transaction_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `loyalty_transaction_uuid` *string, required* — UUID of the Loyalty Transaction that was generated when the Reward was claimed by the Contact. **Response Example** ```json { "data": { "uuid": "13ac4cc1-3522-4dfc-ae4a-a821e5090108", "status": "COLLECTED" }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `6003` | Reward not found. | | `80300` | Reward not found. | | `80300` | Reward is expired. | ## Reverse Collectable Reward Redemption Reverses a previous Collectable Reward Redemption, in the event of an order return or some other cancellation. ```http POST https://api.leat.com/api/v3/oauth/clients/collectable-rewards/{{collectable_reward_redemption_uuid}}/reverse ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `uuid` *string, required* — The UUID of the Collectable Reward Redemption to be reversed **Response Example** ```json { "data": { "uuid": "13ac4cc1-3522-4dfc-ae4a-a821e5090108", "status": "REVERSED", }, "meta": {} } ``` ## Related - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List or update your Rewards - [Loyalty Transactions](https://docs.leat.com/v3/oauth/loyalty-transactions.md) — List your Loyalty Transactions by filtering them on Contact, Shop or Loyalty Transaction type. Source: https://docs.leat.com/v3/oauth/collectable-rewards --- # Contact Attributes Piggy provides a number of standard Contact Attributes, such as first and last name, birthdate, email address and phone number. However, any number of custom contact attributes that are relevant for your business can be created as well. For example, a pet shop owner might add a `favorite_pet_food`Contact Attribute. ## List Contact Attributes Display a list of Contact Attributes for your Account, including both the default ones and any Custom Attributes you've created. ```http GET https://api.leat.com/api/v3/oauth/clients/contact-attributes ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `entity` *string, required* — The type of Custom Attributes you want to retrieve. **Response Example** ```json { "data": [ { "id": 2204, "name": "avatar", "label": "Avatar", "type": "media_upload", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": false, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "An image for the contact", "options": [], "position": 1, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": null, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2205, "name": "firstname", "label": "First name", "type": "text", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": false, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The contact's first name", "options": [], "position": 2, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-09 14:52:24.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2206, "name": "lastname", "label": "Last name", "type": "text", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": false, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The contact's last name", "options": [], "position": 3, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 11:55:39.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2203, "name": "email", "label": "Email", "type": "email", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": true, "field_type": "default", "has_unique_value": true, "description": "The contact's email address", "options": [], "position": 4, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2220, "name": "previous_loyalty_balance", "label": "Previous credit balance", "type": "number", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The value of the credit balance before the last transaction was applied.", "options": [], "position": 5, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2219, "name": "loyalty_balance", "label": "Credit balance", "type": "number", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "Current credit balance", "options": [], "position": 6, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2223, "name": "loyalty_number_of_transactions", "label": "Number of transactions", "type": "number", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The number of loyalty transaction the contact has had", "options": [], "position": 7, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2221, "name": "loyalty_first_transaction_date", "label": "First credit reception date", "type": "date_time", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": true, "field_type": "default", "has_unique_value": false, "description": "The date that the contact got their first points", "options": [], "position": 8, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-06 08:30:08.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2222, "name": "loyalty_last_transaction_date", "label": "Last transaction date", "type": "date_time", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The date of thecontact's most recent loyalty transaction", "options": [], "position": 9, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-09 19:24:58.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2207, "name": "address", "label": "Address", "type": "text", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": false, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The contact's address information", "options": [], "position": 10, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": null, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2208, "name": "phonenumber", "label": "Telephone number", "type": "phone", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": false, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The contact's phone number", "options": [], "position": 11, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": null, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2210, "name": "channel", "label": "Channel", "type": "select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": true, "field_type": "default", "has_unique_value": false, "description": "The channel through which this contact was created", "options": [ { "value": "BUSINESS_APP", "label": "BUSINESS_APP", "description": null, "media": null }, { "value": "API", "label": "API", "description": null, "media": null }, { "value": "BUSINESS_DASHBOARD", "label": "BUSINESS_DASHBOARD", "description": null, "media": null }, { "value": "CUSTOMER_APP", "label": "CUSTOMER_APP", "description": null, "media": null }, { "value": "WEBSHOP_API", "label": "WEBSHOP_API", "description": null, "media": null }, { "value": "IMPORT", "label": "IMPORT", "description": null, "media": null }, { "value": "WEBSITE_FORM", "label": "WEBSITE_FORM", "description": null, "media": null }, { "value": "REGISTER_API", "label": "REGISTER_API", "description": null, "media": null }, { "value": "OAUTH_API", "label": "OAUTH_API", "description": null, "media": null }, { "value": "SUBSCRIPTION_TYPE", "label": "SUBSCRIPTION_TYPE", "description": null, "media": null }, { "value": "CUSTOM_APP", "label": "CUSTOM_APP", "description": null, "media": null }, { "value": "AUTOMATIONS", "label": "AUTOMATIONS", "description": null, "media": null }, { "value": "FORMS", "label": "FORMS", "description": null, "media": null }, { "value": "WIDGET", "label": "WIDGET", "description": null, "media": null } ], "position": 13, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2211, "name": "age", "label": "Age", "type": "number", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": false, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The contact's age", "options": [], "position": 14, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": null, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2212, "name": "locale", "label": "Preferred language", "type": "select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": false, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The contact's preferred language", "options": [ { "value": "en", "label": "English", "description": null, "media": null }, { "value": "nl", "label": "Nederlands", "description": null, "media": null }, { "value": "de", "label": "Deutsch", "description": null, "media": null }, { "value": "es", "label": "Español", "description": null, "media": null }, { "value": "fr", "label": "Francés", "description": null, "media": null } ], "position": 15, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": null, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2213, "name": "status", "label": "Status", "type": "select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The contact's current status", "options": [ { "value": "ACTIVE", "label": "Active", "description": null, "media": null }, { "value": "INACTIVE", "label": "Inactive", "description": null, "media": null } ], "position": 16, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2214, "name": "updated_at", "label": "Updated at", "type": "date_time", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The date the contact was last updated", "options": [], "position": 17, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2215, "name": "created_at", "label": "Created at", "type": "date_time", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": true, "field_type": "default", "has_unique_value": false, "description": "The date on which the contact was created", "options": [], "position": 18, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2216, "name": "is_anonymous", "label": "Anonymous", "type": "boolean", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "This contact has not been verified and uses a fictive email address", "options": [ { "value": "true", "label": "True", "description": null, "media": null }, { "value": "false", "label": "False", "description": null, "media": null } ], "position": 19, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2217, "name": "prepaid_balance", "label": "Prepaid balance", "type": "float", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The contact's current prepaid balance", "options": [], "position": 20, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2218, "name": "loyalty_associated_shops", "label": "Associated shops", "type": "multi_select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "All of the shops where this contact has had loyalty transactions", "options": [ { "value": "123123", "label": "Krusty Krab Downtown", "description": null, "media": { "type": "image", "value": "https://static.piggy.nl/business/profile_types/on_site.svg" } }, { "value": "12312312312312", "label": "Chum Bucket Online", "description": null, "media": { "type": "image", "value": "https://static.piggy.nl/business/profile_types/webshop.svg" } }, { "value": "2222e1242t3324535", "label": "Conch Street", "description": null, "media": { "type": "image", "value": "https://static.piggy.nl/business/profile_types/on_site.svg" } }, ], "position": 21, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-06 08:30:08.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2224, "name": "loyalty_total_credits_received", "label": "Total credits received", "type": "number", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The total number of credits the contact have received to date", "options": [], "position": 22, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2225, "name": "tier", "label": "Tier", "type": "select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": true, "field_type": "default", "has_unique_value": false, "description": "The tier the contact is assigned to", "options": [ { "value": "1", "label": "Gold", "description": "Krusty Krew Members only", "media": null } ], "position": 23, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-06 08:30:08.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2226, "name": "subscription_status_3", "label": "Subscription status: Monthly offers", "type": "select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The current status for the Monthly offers subscription type.", "options": [ { "value": "UNSUBSCRIBED", "label": "Unsubscribed", "description": null, "media": null }, { "value": "PENDING_CONFIRMATION", "label": "Pending confirmation", "description": null, "media": null }, { "value": "SUBSCRIBED", "label": "Subscribed", "description": null, "media": null } ], "position": 24, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2227, "name": "subscription_status_1", "label": "Subscription status: Marketing", "type": "select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The current status for the Marketing subscription type.", "options": [ { "value": "UNSUBSCRIBED", "label": "Unsubscribed", "description": null, "media": null }, { "value": "PENDING_CONFIRMATION", "label": "Pending confirmation", "description": null, "media": null }, { "value": "SUBSCRIBED", "label": "Subscribed", "description": null, "media": null } ], "position": 25, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2228, "name": "subscription_status_5", "label": "Krusty Krab News", "type": "select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The current status for the Product updates subscription type.", "options": [ { "value": "UNSUBSCRIBED", "label": "Unsubscribed", "description": null, "media": null }, { "value": "PENDING_CONFIRMATION", "label": "Pending confirmation", "description": null, "media": null }, { "value": "SUBSCRIBED", "label": "Subscribed", "description": null, "media": null } ], "position": 26, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, { "id": 2229, "name": "subscription_status_4", "label": "Subscription status: Events", "type": "select", "group_name": null, "group": { "id": 140, "name": "profile", "position": 1 }, "is_piggy_defined": true, "is_soft_read_only": true, "is_hard_read_only": false, "field_type": "default", "has_unique_value": false, "description": "The current status for the Events subscription type.", "options": [ { "value": "UNSUBSCRIBED", "label": "Unsubscribed", "description": null, "media": null }, { "value": "PENDING_CONFIRMATION", "label": "Pending confirmation", "description": null, "media": null }, { "value": "SUBSCRIBED", "label": "Subscribed", "description": null, "media": null } ], "position": 27, "created_at": "2023-11-03T14:58:25+00:00", "meta": [], "can_be_deleted": false, "last_used_date": { "date": "2023-11-10 13:32:51.000000", "timezone_type": 3, "timezone": "UTC" }, "created_by_user": { "id": 159, "email": "larry@bikinibottom.sea", "firstname": "Patrick", "lastname": "Star" } }, ], "meta": [] } ``` ## Create Contact Attribute This method allows you to create your own custom Contact Attribute. After the request, you get the created attribute as a response. ```http POST https://api.leat.com/api/v3/oauth/clients/contact-attributes ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — The unique value for the Contact Attribute, used for internal purposes. - `label` *string, optional* — The label for the attribute, which is outputted. - `data_type` *string, required* — The data type of the attribute, possible options: url, text, date, phone, float, color, email, number, select, boolean, rich_text, date_time, long_text, date_range, time_range, identifier, birth_date, file_upload, media_upload, multi_select or license_plate. - `description` *string, optional* — A description for the attribute. - `options` *array, optional* — Applicable for (multi)selects only. Array consisting of label and value arrays. **Response Example** ```json { "data": { "name": "favorite_color", "label": "Your Favorite Color", "description": "some description", "type": "select", "field_type": "select", "is_soft_read_only": false, "is_hard_read_only": false, "is_piggy_defined": false, "options": [ { "label": "Blue", "value": "blue" }, { "label": "Red", "value": "red" }, { "label": "Yellow", "value": "yellow" } ] }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60009` | Attribute already exists. | ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/contact-attributes --- # Contact Identifiers Mostly, Contacts are identified by their email address. However, Contact Identifiers can also be used to retrieve a Contact's information. Most current Contact Identifiers are Loyalty Cards, which have been around since the start of Piggy. However, if needed, virtually anything can be used as a Contact Identifier, provided they use a unique value (within the Account). ## Find Contact Identifier Finds the Contact Identifier. Returns `contact`as null if not yet linked to a Contact. ```http GET https://api.leat.com/api/v3/oauth/clients/contact-identifiers/find?contact_identifier_value={{contact_identifier_value}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_identifier_value` *string, required* — The unique value for the Contact Identifier. **Response Example** ```json { "data": { "name": "Krabby Membership", "value": "ABCDE12356-90", "active": true, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60003` | Contact identifier not found. | | `60004` | Contact identifier inactive. | ## Create Contact Identifier Creates a Contact Identifier. You can link it directly to a Contact when you send a `contact_uuid` along. ```http POST https://api.leat.com/api/v3/oauth/clients/contact-identifiers ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_identifier_value` *string, required* — The unique value for the Contact Identifier, which will serve as the identifier. - `contact_uuid` *string, optional* — The Contact's UUID for whom the identifier is to be created. If you want to link a contact later, you can leave this empty. - `contact_identifier_name` *string, optional* — A name for the Contact Identifier. When left out, it receives the same value as contact_identifier_value. **Response Example** ```json { "data": { "name": "Krabby Membership", "value": "ABCDE12356-90", "active": true, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `60006` | Contact identifier already exists. | ## Link Contact Identifier Links an existing, unlinked Contact Identifier to an existing Contact. ```http PUT https://api.leat.com/api/v3/oauth/clients/contact-identifiers/link ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_identifier_value` *string, required* — The unique value of the Contact Identifier that you want to link to the Contact. - `contact_uuid` *string, required* — The Contact UUID that needs to be linked to the Contact Identifier. **Response Example** ```json { "data": { "name": "Krabby Membership", "value": "ABCDE12356-90", "active": true, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60003` | Contact identifier not found. | | `60004` | Contact identifier inactive. | | `55031` | Contact not found. | | `60014` | Contact identifier is already linked. | ## Unlink Contact Identifier Removes a linked Contact from a Contact Identifier. ```http PUT https://api.leat.com/api/v3/oauth/clients/contact-identifiers/unlink ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_identifier_value` *string, required* — The unique value of the Contact Identifier that you want to unlink the contact from. **Response Example** ```json { "data": { "name": "Krabby Membership", "value": "ABCDE12356-90", "active": true, "contact": null }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60003` | Contact identifier not found. | | `60004` | Contact identifier inactive. | ## Update Contact Identifier Updates a Contact Identifier. Note: only the identifier's `name` and `active` properties can be updated. The value cannot be updated. ```http PUT https://api.leat.com/api/v3/oauth/clients/contact-identifiers ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_identifier_value` *string, required* — The unique value of the Contact Identifier. - `contact_identifier_name` *string, optional* — A name for the Contact Identifier. - `contact_identifier_active` *boolean, optional* — Active state of the contact identifier. **Response Example** ```json { "data": { "name": "Krabby Membership", "value": "ABCDE12356-90", "active": true, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `60006` | Contact identifier already exists. | ## Delete Contact Identifier Deletes an Contact Identifier. ```http DELETE https://api.leat.com/api/v3/oauth/clients/contact-identifiers ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_identifier_value` *string, required* — The unique value of the Contact Identifier that you want to delete. **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60003` | Contact identifier not found. | ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards Source: https://docs.leat.com/v3/oauth/contact-identifiers --- # Contact Session Tokens ## Find Contact Session Token Find a Contact's Session Token to identify and validate them. ```http GET https://api.leat.com/api/v3/oauth/clients/contact-session-tokens/{{contact_session_token}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "token": "12345abcdefg", "contact": { "uuid": "123abc-def789-qwerty-34567" "email": "spongebob@bikinibottom.sea }, "status": "active", }, "meta": [] } ``` ## Use Contact Session Token Mark a session token as used, making it invalid for next attempts. ```http POST https://api.leat.com/api/v3/oauth/clients/contact-session-tokens/{{contact_session_token}}/use ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "token": "12345abcdefg", "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea }, "status": "used", }, "meta": [] } ``` ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards Source: https://docs.leat.com/v3/oauth/contact-session-tokens --- # Contact Subscriptions Contact Subscriptions are the links between Subscription Types and Contacts. A Contact is either subscribed or unsubscribed, which decides whether or not they will receive emails sent through that Subscription Type. However, in the case of a Subscription Type with a double opt-in strategy, a third option comes into the mix: pending confirmation. In terms of whether they will receive emails for the Subscription Type, they will not. It is also not possible to fully subscribe a Contact for a double opt-in Subscription Type. ## List Contact Subscriptions This function retrieves a comprehensive list of all subscriptions associated with a specific Contact, identified by their UUID. It provides an easy and efficient way to view the various subscriptions a Contact is enrolled in, ensuring you have up-to-date information on their engagement with your services. ```http GET https://api.leat.com/api/v3/oauth/clients/contact-subscriptions/{{contact_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — UUID of the Contact for whom the Subscriptions are to be retrieved. **Response Example** ```json { "data": [ { "is_subscribed": false, "subscription_type": { "id": 2, "uuid" : null, "name": "Functional", "description": "Functionele emails", "active": true, "strategy": "OPT_OUT" }, "status": 0 }, { "is_subscribed": false, "subscription_type": { "id": 1, "uuid" : null, "name": "Marketing", "description": "Marketing", "active": true, "strategy": "DOUBLE_OPT_IN" }, "status": 0 } ], "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `55031` | Contact not found. | ## Subscribe Contact This call enables the subscription of a specific Contact to a chosen Subscription Type. By providing the Contact's UUID and the desired Subscription Type's UUID, you can easily enroll a Contact in a particular subscription. ```http PUT https://api.leat.com/api/v3/oauth/clients/contact-subscriptions/{{contact_uuid}}/subscribe ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — UUID of the Contact to be subscribed. **Body** - `subscription_type_uuid` *string, required* — The Subscription Type's UUID. **Response Example** ```json { "data": { "is_subscribed": false, "status": "PENDING_CONFIRMATION", "subscription_type": { "name": "Marketing", "description": "Subscribe here for marketing purposes", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": "123123123", "id": 1 } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `55030` | Subscription type not found. | ## Unsubscribe Contact to Subscription Type This function allows for the removal of a specific Contact from a particular Subscription Type. By specifying the Contact's UUID and the Subscription Type's UUID, you can easily manage the Contact's subscription status. ```http PUT https://api.leat.com/api/v3/oauth/clients/contact-subscriptions/{{contact_uuid}}/unsubscribe ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — UUID of the Contact to be unsubscribed. **Body** - `subscription_type_uuid` *string, required* — The Subscription Type's UUID. **Response Example** ```json { "data": { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Marketing", "description": "Subscribe here for marketing purposes", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": "123123123", "id": 1 } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `55030` | Subscription type not found. | ## Related - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards - [Reward Receptions](https://docs.leat.com/v3/oauth/reward-receptions.md) — It's what Contacts are saving up for: Reward Receptions Source: https://docs.leat.com/v3/oauth/contact-subscriptions --- # Contact Verification > **Tip** > > If you already have a verification method in your own application – either through login, a saved uuid, magic link etc. - we recommend you use that one instead. Business owners use the Piggy system to send out a verification email to their Contacts whenever a Contact wants to login to the business owner's Contacts Portal. This verification is configured in the Business Dashboard and sends out a code, which the Contact in question needs to fill in. For integrations lacking a method to authenticate email addresses or Contacts, the API provides a solution for sending verification emails and validating the received code. ## Send Contact Verification Email This function triggers the sending of a verification email to a Contact, provided they exist in the system. Therefore, it requires the Contact's email address to proceed. When doing so, the process of verifying their identity through email validation is initiated. The system checks for the existence of the Contact and the associated Contacts Portal before sending the email. If the Contact is not found or if the Contacts Portal is missing, the process will not proceed. ```http POST https://api.leat.com/api/v3/oauth/clients/contact-verification/send ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `email` *string, required* — The email address of the Contact to receive the email. **Response Example** ```json { "data": { "type": "Success", "message": "An email containing a link to verify your email has been sent to spongebob@bikinibottom.sea. Please check your spam / junk as well." }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `55032` | User app not found. | ## Verify Login Code Validates the login code received by the Contact via email. This verification is a crucial step to ensure the Contact's identity and secure login. The function checks for the presence of the Contacts Portal and uses the provided code and email to complete the verification process. ```http POST https://api.leat.com/api/v3/oauth/clients/contact-verification/verify ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `code` *string, required* — The code sent by email. - `email` *string, required* — The Contact's email. **Response Example** ```json { "data": { "uuid": "123abc-lmnopq-3242a-8899aa" }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55032` | User app not found. | | `13010` | Passcode not found. | ## Get Auth Token Retrieves an authentication token for a specific Contact, identified by their UUID. Allowing verification of the Contact's identity and granting secure access. ```http GET https://api.leat.com/api/v3/oauth/clients/contact-verification/auth-token/{{contact_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID. **Response Example** ```json { "data": { "auth_token": "eyJpdiI6IkluSk9sRzNBRFFFOUFNNUh0eDZTWGc9PSIsInZhbHVlIjoiN0Nna05yZFFEbE16YjY0ME55dWcvTDZVVUl5WGdVN2cxRDJjcjl6K1lzUmxoWlYxZUVXMCt3TCtRUmNQc0g1SG9YR2dnd0wwM1N2RE54WDZhcnR2Z0FxMFZkRXNoUXN3eDN3OC9RZmgreEJ1clFyOS82eUhBeUZVWGNDdytVd2tRM01lWVJPZi90MTZkT3hXRXQwQXNnPT0iLCJtYWMiOiIyNmFlYzA0YzJjMDI3NGM4MDI2YzMyMTAzYzJmZmQ3YWEzY2VhNWZjMDgxYzQ3NGYwNThjZWY1MTJlZTdmNmVlIiwidGFnIjoiIn0=" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `55031` | Contact not found. | | `1053` | Account secret is invalid | | `3000` | The token is expired | ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards Source: https://docs.leat.com/v3/oauth/contact-verification --- # Contacts Contacts are the end-users of Clients. Contacts are provided with basic attributes, such as an email address, first name, last name, and so forth. Any custom attributes can also be created, either in the Business Dashboard or via API. Their email address is the standard unique identifier, via which Contacts can be found. Aside from the email address, Contact Identifiers can also be used to find a Contact. ## List Contacts Retrieve a list of Contacts with pagination. The pagination has default values, but you can alter those by supplying the `page`and `limit`parameters including values with the call. Filter on contact attributes with `attributes[name][operator]=value`, where name is the attribute's `name` from [List Contact Attributes](https://docs.leat.com/v3/oauth/contact-attributes.md#list-contact-attributes), for example `attributes[likes_pizza]=true`, `attributes[visits][gte]=10` or `attributes[color][in]=red,blue`. A value without an operator means `eq`, and a Contact has to match every filter. The operators an attribute takes depend on its type: - `number, float:` eq, neq, gt, gte, lt, lte, known, unknown - `date, date_time:` eq, neq, gt, gte, lt, lte, known, unknown. The value is an ISO 8601 date, and it is compared as a whole day in your account's timezone. - `text, long_text, rich_text, email, phone, url, color, identifier, license_plate:` eq, neq, contains, not\_contains, known, unknown - `select:` eq, neq, in, not\_in, known, unknown - `multi_select:` eq, in, not\_in, known, unknown. Here eq matches Contacts with exactly the given values. - `boolean:` eq with true or false, unknown - `birth_date, file_upload, media_upload:` known, unknown Attributes of type `date_range` and `time_range` cannot be filtered on. Text and select values match regardless of case and accents. Each Contact carries `current_values`: its fields and contact attributes as stored for filtering. It holds `id` (the Contact's id, also under `contact_id`), `uuid`, `email`, `status`, `created_at` and `updated_at` in UTC, the loyalty totals, `tier` (the id of the current Tier), the ids of the Contact's lists and segments, a `subscription_status_` per Subscription Type, and every contact attribute under its `name`. Values keep their stored format: datetimes as `Y-m-d H:i:s`, dates as `Y-m-d`, booleans as `0` or `1`, multi-select values as a JSON string such as `["pink", "green"]`, and `null` when there is no value. `current_display_values` repeats `current_values` on this endpoint. > **Warning** > > **Please note:** the `attributes` array in the response is now deprecated. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `limit` *number, optional* — Limit of records to retrieve. Default: 10, maximum: 100. - `page` *number, optional* — Page to retrieve. Default: 1. - `sort` *string, optional* — Sort by column. Ascending by default, prefix with a hyphen to sort descending (e.g. ?sort=-created_at) Possible values: - `created_at` — 'Sort by creation date. Prefix with '-' for descending. - `created_at` *array, optional* — Filter on creation date, can only be sent in as array. Date value must be in ISO8601 format. Combine to retrieve contacts created between two dates. Possible values: - `created_at[gt]` — Filter on creation date is greater than value. - `created_at[gte]` — Filter on creation date is greater than or equal to value. - `created_at[lt]` — Filter on creation date is lower than value. - `created_at[lte]` — Filter on creation date is lower than or equal to value. - `attributes` *array, optional* — Filter on contact attributes, keyed by attribute name: attributes[name][operator]=value. A value without an operator means eq. Combine filters to narrow the list further; a Contact has to match all of them. Possible values: - `attributes[name][eq]` — Equal to value. For a date, the same day. For a multi_select, exactly the given values, comma separated. For a boolean, true or false. - `attributes[name][neq]` — Not equal to value. - `attributes[name][gt]` — Greater than value. For a date, after that day. - `attributes[name][gte]` — Greater than or equal to value. For a date, from that day on. - `attributes[name][lt]` — Lower than value. For a date, before that day. - `attributes[name][lte]` — Lower than or equal to value. For a date, up to and including that day. - `attributes[name][contains]` — Contains value. - `attributes[name][not_contains]` — Does not contain value. - `attributes[name][in]` — Has any of the given values, comma separated. - `attributes[name][not_in]` — Has none of the given values, comma separated. - `attributes[name][known]` — Has a value. The value you send is ignored. - `attributes[name][unknown]` — Has no value. The value you send is ignored. **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55019` | Custom attribute not found. | **Response Example** ```json { "data": [ { "id": 21, "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea", "attributes": [], "tier": null, "subscriptions": [ { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "id": 1, "uuid": "123123123", "name": "Marketing", "active": true, "description": "Subscribe here for marketing purposes", "strategy": "DOUBLE_OPT_IN" } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "id": 3, "uuid": null, "name": "Bikini Bottom Deals", "active": true, "description": "A monthly email blast with our best offers!", "strategy": "DOUBLE_OPT_IN" } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "id": 4, "uuid": null, "name": "Events", "active": true, "description": "Stay in the loop for all our special events", "strategy": "OPT_IN" } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "id": 5, "uuid": null, "name": "Krusty Krab News", "active": true, "description": "Never miss out on any of our newest features", "strategy": "OPT_OUT" } } ], "credit_balance": { "id": 2625749, "balance": 0 }, "prepaid_balance": { "balance_in_cents": 0 }, "current_values": { "id": 21, "contact_id": 21, "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea", "channel": "OAUTH_API", "locale": "en", "status": "ACTIVE", "contact_type_option_id": null, "updated_at": "2023-11-13 12:56:00", "created_at": "2021-10-20 18:06:09", "is_anonymous": 0, "prepaid_balance": 0.0, "loyalty_associated_shops": "[]", "loyalty_balance": 0, "loyalty_total_purchase_amount": 0, "previous_loyalty_balance": 0, "loyalty_first_transaction_date": null, "loyalty_last_transaction_date": null, "loyalty_last_transaction_credits": null, "loyalty_number_of_transactions": 0, "loyalty_total_credits_received": 0, "default_contact_identifier": null, "tier": null, "present_in_imports": "", "list_memberships": "", "segment_memberships": "[]", "number_of_referred_contacts": 0, "number_of_completed_referrals": 0, "added_at": "2021-10-20 18:06:09", "last_updated_at": "2023-11-13 12:56:00", "subscription_status_1": "UNSUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "firstname": "SpongeBob", "lastname": "SquarePants", "phonenumber": null, "address": null, "birthdate": "1986-07-14", "avatar": null, "city": "Bikini Bottom", "postalcode": "9999YZ", "housenumber": 124, "favorite_color": "[]", "likes_pizza": null }, "current_display_values": { "id": 21, "contact_id": 21, "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea", "channel": "OAUTH_API", "locale": "en", "status": "ACTIVE", "contact_type_option_id": null, "updated_at": "2023-11-13 12:56:00", "created_at": "2021-10-20 18:06:09", "is_anonymous": 0, "prepaid_balance": 0.0, "loyalty_associated_shops": "[]", "loyalty_balance": 0, "loyalty_total_purchase_amount": 0, "previous_loyalty_balance": 0, "loyalty_first_transaction_date": null, "loyalty_last_transaction_date": null, "loyalty_last_transaction_credits": null, "loyalty_number_of_transactions": 0, "loyalty_total_credits_received": 0, "default_contact_identifier": null, "tier": null, "present_in_imports": "", "list_memberships": "", "segment_memberships": "[]", "number_of_referred_contacts": 0, "number_of_completed_referrals": 0, "added_at": "2021-10-20 18:06:09", "last_updated_at": "2023-11-13 12:56:00", "subscription_status_1": "UNSUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "firstname": "SpongeBob", "lastname": "SquarePants", "phonenumber": null, "address": null, "birthdate": "1986-07-14", "avatar": null, "city": "Bikini Bottom", "postalcode": "9999YZ", "housenumber": 124, "favorite_color": "[]", "likes_pizza": null } }, { "id": 22, "uuid": "1b7e3c4a-5f2d-4e8b-9a6c-0d2f4e6a8b1c", "email": "patrick@bikinibottom.sea", "attributes": [], "tier": { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "name": "Krusty Krew", "description": "Krusty Krew Members only", "position": 1 }, "subscriptions": [ { "is_subscribed": true, "status": "SUBSCRIBED", "subscription_type": { "id": 1, "uuid": "123123123", "name": "Marketing", "active": true, "description": "Subscribe here for marketing purposes", "strategy": "DOUBLE_OPT_IN" } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "id": 3, "uuid": null, "name": "Bikini Bottom Deals", "active": true, "description": "A monthly email blast with our best offers!", "strategy": "DOUBLE_OPT_IN" } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "id": 4, "uuid": null, "name": "Events", "active": true, "description": "Stay in the loop for all our special events", "strategy": "OPT_IN" } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "id": 5, "uuid": null, "name": "Krusty Krab News", "active": true, "description": "Never miss out on any of our newest features", "strategy": "OPT_OUT" } } ], "credit_balance": { "id": 2625740, "balance": 2895 }, "prepaid_balance": { "balance_in_cents": 4000 }, "current_values": { "id": 22, "contact_id": 22, "uuid": "1b7e3c4a-5f2d-4e8b-9a6c-0d2f4e6a8b1c", "email": "patrick@bikinibottom.sea", "channel": "BUSINESS_APP", "locale": "en", "status": "ACTIVE", "contact_type_option_id": null, "updated_at": "2023-11-13 14:37:04", "created_at": "2022-01-09 15:03:12", "is_anonymous": 0, "prepaid_balance": 40.0, "loyalty_associated_shops": "[\"5fd3322e-671e-430f-aa38-5ed02e600ff7\"]", "loyalty_balance": 2895, "loyalty_total_purchase_amount": 61850, "previous_loyalty_balance": 2897, "loyalty_first_transaction_date": "2023-01-09 15:03:37", "loyalty_last_transaction_date": "2023-11-13 14:37:04", "loyalty_last_transaction_credits": -2, "loyalty_number_of_transactions": 77, "loyalty_total_credits_received": 3014, "default_contact_identifier": "123123", "tier": "1", "present_in_imports": "", "list_memberships": "4;7", "segment_memberships": "[\"12\"]", "number_of_referred_contacts": 0, "number_of_completed_referrals": 0, "added_at": "2022-01-09 15:03:12", "last_updated_at": "2023-11-13 14:37:04", "subscription_status_1": "SUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "firstname": "Patrick", "lastname": "Star", "phonenumber": "+31612345678", "address": null, "birthdate": null, "avatar": null, "city": "Bikini Bottom", "postalcode": "9999YZ", "housenumber": 120, "favorite_color": "[\"pink\", \"green\"]", "likes_pizza": 1 }, "current_display_values": { "id": 22, "contact_id": 22, "uuid": "1b7e3c4a-5f2d-4e8b-9a6c-0d2f4e6a8b1c", "email": "patrick@bikinibottom.sea", "channel": "BUSINESS_APP", "locale": "en", "status": "ACTIVE", "contact_type_option_id": null, "updated_at": "2023-11-13 14:37:04", "created_at": "2022-01-09 15:03:12", "is_anonymous": 0, "prepaid_balance": 40.0, "loyalty_associated_shops": "[\"5fd3322e-671e-430f-aa38-5ed02e600ff7\"]", "loyalty_balance": 2895, "loyalty_total_purchase_amount": 61850, "previous_loyalty_balance": 2897, "loyalty_first_transaction_date": "2023-01-09 15:03:37", "loyalty_last_transaction_date": "2023-11-13 14:37:04", "loyalty_last_transaction_credits": -2, "loyalty_number_of_transactions": 77, "loyalty_total_credits_received": 3014, "default_contact_identifier": "123123", "tier": "1", "present_in_imports": "", "list_memberships": "4;7", "segment_memberships": "[\"12\"]", "number_of_referred_contacts": 0, "number_of_completed_referrals": 0, "added_at": "2022-01-09 15:03:12", "last_updated_at": "2023-11-13 14:37:04", "subscription_status_1": "SUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "firstname": "Patrick", "lastname": "Star", "phonenumber": "+31612345678", "address": null, "birthdate": null, "avatar": null, "city": "Bikini Bottom", "postalcode": "9999YZ", "housenumber": 120, "favorite_color": "[\"pink\", \"green\"]", "likes_pizza": 1 } } ], "meta": { "page": 1, "limit": 2, "viewing_from": 1, "viewing_to": 2, "last_page": 31, "total": 62 } } ``` ## Create Contact If a Contact needs to be created, only the email address is needed. To add any additional data to the Contact, a subsequent [Update Contact](#update-contact) API call can be used. ```http POST https://api.leat.com/api/v3/oauth/clients/contacts ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `email` *string, required* — The email address of the Contact to be created. - `referral_code` *string, optional* — A referral code used for Piggy's referral programme. **Response Example** ```json { "data": { "uuid": "cd183615-d1cb-402a-b618-6472d5e13890" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60005` | Contact already exists. | ## Create Contact Async If a Contact needs to be created *quickly* and any system values that are normally created upon creation aren't relevant, this function can be used. This API call is much faster than the 'regular' creation API call, focusing on basic Contact creation. ```http POST https://api.leat.com/api/v3/oauth/clients/contacts/async ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `email` *string, required* — The email address of the Contact to be created. - `referral_code` *string, optional* — A referral code used for Piggy's referral programme. **Response Example** ```json { "data": { "uuid": "cd183615-d1cb-402a-b618-6472d5e13890" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60005` | Contact already exists. | | `60000` | Usage limit reached. | ## Create Anonymous Contact Creates an anonymous Contact with a fictitious email address. This can be used whenever a new Contact cannot (yet) be identified by their email address, but some actions are already desired (e.g. creation of Credit Receptions). The Contact in question can later create a Contact with their actual email address, and subsequently 'claim' the anonymous Contact, which is then merged with the new Contact. ```http POST https://api.leat.com/api/v3/oauth/clients/contacts/anonymous ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_identifier_value` *string, optional* — The unique value for the Contact Identifier, which will serve as the identifier **Response Example** ```json { "data": { "uuid": "cd183615-d1cb-402a-b618-6472d5e13890" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60005` | Contact already exists. | | `60014` | Contact identifier is already linked. | | `60003` | Contact identifier not found. | | `60000` | Usage limit reached. | ## Find Contact Find a Contact by email address. To find a Contact using an identifier, see [Contact Identifiers.](https://docs.leat.com/v3/oauth/contact-identifiers.md) > **Warning** > > **Please note:** the `attributes` array is now deprecated. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/find-one-by ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `email` *string, required* — The Contact's email address **Response Example** ```json { "data": { "id": 22, "uuid": "123", "email": "spongebob@bikinibottom.sea", "tier": { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "name": "Krusty Krew", "description": "Krusty Krew Members only", "position": 1, "media": null }, "subscriptions": [ { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Marketing", "description": "Subscribe here for marketing purposes", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": "123123123", "id": 1 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Bikini Bottom Deals", "description": "A monthly email blast with our best offers!", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": null, "id": 3 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Events", "description": "Stay in the loop for all our special events", "active": true, "strategy": "OPT_IN", "uuid": null, "id": 4 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Krusty Krab News", "description": "Never miss out on any of our newest features", "active": true, "strategy": "OPT_OUT", "uuid": null, "id": 5 } } ], "credit_balance": { "balance": 1612, }, "prepaid_balance": { "balance_in_cents": 2000 }, "current_values": { "age": null, "city": "Bikini Bottom", "dier": null, "tier": "1", "uuid": "123", "email": "spongebob@bikinibottom.sea", "avatar": "", "locale": null, "status": "ACTIVE", "address": "", "channel": null, "lastname": "", "birthdate": null, "firstname": "", "created_at": "2021-10-20T18:06:09+00:00", "postalcode": "9999YZ", "updated_at": "2023-11-06T16:47:00+00:00", "housenumber": 12, "phonenumber": "", "is_anonymous": false, "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=123", "a_custom_attribute": null, "loyalty_balance": 1612, "prepaid_balance": 20, "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Ikh3ZW8yRW9pSU9Gc25SYmxRUFVVWUE9PSIsInZhbHVlIjoianhuYncxVHBlK0dqMTN4RHhXNzdZNVJ3aGJqcERFdmVWQlcxYjFNZVZYSVdoWVVMWk54K1UvQ2lFek1nMmtGaTJZNGRNOG1rUUxwRGNnU25JcitpN0d5RWVKN21kS1gvSmMvNTA2bmxqU0Z0cmxoR2pPT0Ewc1hXeEJsMkVJNGFDQlNlSWpHRXVpWndyd3g3eTVXck90Z0tlL3J2RHNRKzRjRkNGb0tmVmVzPSIsIm1hYyI6ImZlMzc0ZDYzZDVjNTNjOTc3YTc1ZTMxN2RmMzM4MDFkZWQ2YWI2MDkwNDY4OWQ1MDk0ZWRkN2Y2ZTdhZjY5ZmEiLCJ0YWciOiIifQ==", "custom_app_login_code": 198169, "subscription_status_1": "UNSUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "loyalty_associated_shops": "123123;5fd3322e-671e-430f-aa38-5ed02e600ff7", "previous_loyalty_balance": 1617, "default_contact_identifier": "hash123456", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/123", "loyalty_last_transaction_date": "2023-11-06T11:32:00+00:00", "loyalty_first_transaction_date": "2023-01-09T15:03:37+00:00", "loyalty_number_of_transactions": 55, "loyalty_total_credits_received": 1697, "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=3" }, "current_display_values": { "age": "", "city": "", "dier": "False", "tier": "Gold", "uuid": "", "email": "spongebob@bikinibottom.sea", "avatar": "", "locale": "", "status": "Active", "address": "", "channel": "", "lastname": "", "birthdate": "", "firstname": "", "created_at": "2021-10-20, 20:06", "postalcode": "", "updated_at": "2023-11-06, 17:47", "housenumber": "", "phonenumber": "", "is_anonymous": "False", "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=123", "a_custom_attribute": "", "loyalty_balance": "1612", "prepaid_balance": "20", "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Imx6TzJVV1ZwNlBRd3VkQmV1NnhKbnc9PSIsInZhbHVlIjoiVnlKZWM4OG5Vdjl0QmNEY242YmZ3OFNpWjQwQXkwRThmMCttWHlwQmNSTFluNzdpVkQwY0ZEcVo5TktvUXRHSVBYUE43S2dscFpyMUJnazFrWWZWVHI3cDNLTVJsZDcwWU9zQWdIejh5YjRQOGx4RjV6aDJjR3FTWlI1RTNlQlRrZEVNcURZeFlaZlYvQ2R2STdSU1RDdFdZT0RRdkxNN01vOXhLQnhaZkNVPSIsIm1hYyI6ImJjMzczNWQ2NGEwNzZlNjZhZTU0OTk3MzIxOTczODYzZjMxZjYwNzM2MjY5ZWJkODRkMTEzODU0NmQxMDU4OTQiLCJ0YWciOiIifQ==", "custom_app_login_code": "198169", "subscription_status_1": "Unsubscribed", "subscription_status_3": "Unsubscribed", "subscription_status_4": "Unsubscribed", "subscription_status_5": "Unsubscribed", "loyalty_associated_shops": "Krusty Krab Downtown and Sesame Street", "previous_loyalty_balance": "1617", "default_contact_identifier": "hash123456", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/123", "loyalty_last_transaction_date": "2023-11-06, 12:32", "loyalty_first_transaction_date": "2023-01-09, 16:03", "loyalty_number_of_transactions": "55", "loyalty_total_credits_received": "1697", "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=3" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Find or Create Contact Find Contact by email address or – if no Contact exists yet for this email address – our API creates a new Contact for the email address you just supplied and returns the newly created Contact in the response. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/find-or-create ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `email` *string, required* — The Contact's email address. **Response Example** ```json { "data": { "id": 22, "uuid": "123", "email": "spongebob@bikinibottom.sea", "tier": { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "name": "Krusty Krew", "description": "Krusty Krew Members only", "position": 1, "media": null }, "subscriptions": [ { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Marketing", "description": "Subscribe here for marketing purposes", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": "123123123", "id": 1 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Bikini Bottom Deals", "description": "A monthly email blast with our best offers!", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": null, "id": 3 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Events", "description": "Stay in the loop for all our special events", "active": true, "strategy": "OPT_IN", "uuid": null, "id": 4 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Krusty Krab News", "description": "Never miss out on any of our newest features", "active": true, "strategy": "OPT_OUT", "uuid": null, "id": 5 } } ], "credit_balance": { "balance": 1612, }, "prepaid_balance": { "balance_in_cents": 2000 }, "current_values": { "age": null, "city": "Bikini Bottom", "dier": null, "tier": "1", "uuid": "123", "email": "spongebob@bikinibottom.sea", "avatar": "", "locale": null, "status": "ACTIVE", "address": "", "channel": null, "lastname": "", "birthdate": null, "firstname": "", "created_at": "2021-10-20T18:06:09+00:00", "postalcode": "9999YZ", "updated_at": "2023-11-06T16:47:00+00:00", "housenumber": 12, "phonenumber": "", "is_anonymous": false, "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=123", "a_custom_attribute": null, "loyalty_balance": 1612, "prepaid_balance": 20, "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Ikh3ZW8yRW9pSU9Gc25SYmxRUFVVWUE9PSIsInZhbHVlIjoianhuYncxVHBlK0dqMTN4RHhXNzdZNVJ3aGJqcERFdmVWQlcxYjFNZVZYSVdoWVVMWk54K1UvQ2lFek1nMmtGaTJZNGRNOG1rUUxwRGNnU25JcitpN0d5RWVKN21kS1gvSmMvNTA2bmxqU0Z0cmxoR2pPT0Ewc1hXeEJsMkVJNGFDQlNlSWpHRXVpWndyd3g3eTVXck90Z0tlL3J2RHNRKzRjRkNGb0tmVmVzPSIsIm1hYyI6ImZlMzc0ZDYzZDVjNTNjOTc3YTc1ZTMxN2RmMzM4MDFkZWQ2YWI2MDkwNDY4OWQ1MDk0ZWRkN2Y2ZTdhZjY5ZmEiLCJ0YWciOiIifQ==", "custom_app_login_code": 198169, "subscription_status_1": "UNSUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "loyalty_associated_shops": "123123;5fd3322e-671e-430f-aa38-5ed02e600ff7", "previous_loyalty_balance": 1617, "default_contact_identifier": "hash123456", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/123", "loyalty_last_transaction_date": "2023-11-06T11:32:00+00:00", "loyalty_first_transaction_date": "2023-01-09T15:03:37+00:00", "loyalty_number_of_transactions": 55, "loyalty_total_credits_received": 1697, "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=3" }, "current_display_values": { "age": "", "city": "", "dier": "False", "tier": "Gold", "uuid": "", "email": "spongebob@bikinibottom.sea", "avatar": "", "locale": "", "status": "Active", "address": "", "channel": "", "lastname": "", "birthdate": "", "firstname": "", "created_at": "2021-10-20, 20:06", "postalcode": "", "updated_at": "2023-11-06, 17:47", "housenumber": "", "phonenumber": "", "is_anonymous": "False", "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=123", "a_custom_attribute": "", "loyalty_balance": "1612", "prepaid_balance": "20", "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Imx6TzJVV1ZwNlBRd3VkQmV1NnhKbnc9PSIsInZhbHVlIjoiVnlKZWM4OG5Vdjl0QmNEY242YmZ3OFNpWjQwQXkwRThmMCttWHlwQmNSTFluNzdpVkQwY0ZEcVo5TktvUXRHSVBYUE43S2dscFpyMUJnazFrWWZWVHI3cDNLTVJsZDcwWU9zQWdIejh5YjRQOGx4RjV6aDJjR3FTWlI1RTNlQlRrZEVNcURZeFlaZlYvQ2R2STdSU1RDdFdZT0RRdkxNN01vOXhLQnhaZkNVPSIsIm1hYyI6ImJjMzczNWQ2NGEwNzZlNjZhZTU0OTk3MzIxOTczODYzZjMxZjYwNzM2MjY5ZWJkODRkMTEzODU0NmQxMDU4OTQiLCJ0YWciOiIifQ==", "custom_app_login_code": "198169", "subscription_status_1": "Unsubscribed", "subscription_status_3": "Unsubscribed", "subscription_status_4": "Unsubscribed", "subscription_status_5": "Unsubscribed", "loyalty_associated_shops": "Krusty Krab Downtown and Sesame Street", "previous_loyalty_balance": "1617", "default_contact_identifier": "hash123456", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/123", "loyalty_last_transaction_date": "2023-11-06, 12:32", "loyalty_first_transaction_date": "2023-01-09, 16:03", "loyalty_number_of_transactions": "55", "loyalty_total_credits_received": "1697", "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=3" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60000` | Usage limit reached. | ## Find or Create Contact Async If a Contact needs to be retrieved or created *quickly* and any system values that are normally created upon contact creation aren't relevant, this can be used. This API call is much faster than the 'regular' find or create API call; only the Contact's UUID will be returned on the response in this instance. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/find-or-create/async ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `email` *string, required* — The email address of the Contact to be created or to be found. **Response Example** ```json { "data": { "uuid": "cd183615-d1cb-402a-b618-6472d5e13890" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60000` | Usage limit reached. | ## Get Contact Retrieve a specific Contact with the the using its UUID. > **Warning** > > **Please note:** the `attributes` array is now deprecated. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID **Response Example** ```json { "data": { "id": 22, "uuid": "123", "email": "spongebob@bikinibottom.sea", "tier": { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "name": "Krusty Krew", "description": "Krusty Krew Members only", "position": 1, "media": null }, "subscriptions": [ { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Marketing", "description": "Subscribe here for marketing purposes", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": "123123123", "id": 1 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Bikini Bottom Deals", "description": "A monthly email blast with our best offers!", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": null, "id": 3 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Events", "description": "Stay in the loop for all our special events", "active": true, "strategy": "OPT_IN", "uuid": null, "id": 4 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Krusty Krab News", "description": "Never miss out on any of our newest features", "active": true, "strategy": "OPT_OUT", "uuid": null, "id": 5 } } ], "credit_balance": { "balance": 1612, }, "prepaid_balance": { "balance_in_cents": 2000 }, "current_values": { "age": null, "city": "Bikini Bottom", "dier": null, "tier": "1", "uuid": "123", "email": "spongebob@bikinibottom.sea", "avatar": "", "locale": null, "status": "ACTIVE", "address": "", "channel": null, "lastname": "", "birthdate": null, "firstname": "", "created_at": "2021-10-20T18:06:09+00:00", "postalcode": "9999YZ", "updated_at": "2023-11-06T16:47:00+00:00", "housenumber": 12, "phonenumber": "", "is_anonymous": false, "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=123", "a_custom_attribute": null, "loyalty_balance": 1612, "prepaid_balance": 20, "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Ikh3ZW8yRW9pSU9Gc25SYmxRUFVVWUE9PSIsInZhbHVlIjoianhuYncxVHBlK0dqMTN4RHhXNzdZNVJ3aGJqcERFdmVWQlcxYjFNZVZYSVdoWVVMWk54K1UvQ2lFek1nMmtGaTJZNGRNOG1rUUxwRGNnU25JcitpN0d5RWVKN21kS1gvSmMvNTA2bmxqU0Z0cmxoR2pPT0Ewc1hXeEJsMkVJNGFDQlNlSWpHRXVpWndyd3g3eTVXck90Z0tlL3J2RHNRKzRjRkNGb0tmVmVzPSIsIm1hYyI6ImZlMzc0ZDYzZDVjNTNjOTc3YTc1ZTMxN2RmMzM4MDFkZWQ2YWI2MDkwNDY4OWQ1MDk0ZWRkN2Y2ZTdhZjY5ZmEiLCJ0YWciOiIifQ==", "custom_app_login_code": 198169, "subscription_status_1": "UNSUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "loyalty_associated_shops": "123123;5fd3322e-671e-430f-aa38-5ed02e600ff7", "previous_loyalty_balance": 1617, "default_contact_identifier": "hash123456", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/123", "loyalty_last_transaction_date": "2023-11-06T11:32:00+00:00", "loyalty_first_transaction_date": "2023-01-09T15:03:37+00:00", "loyalty_number_of_transactions": 55, "loyalty_total_credits_received": 1697, "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=3" }, "current_display_values": { "age": "", "city": "", "dier": "False", "tier": "Gold", "uuid": "", "email": "spongebob@bikinibottom.sea", "avatar": "", "locale": "", "status": "Active", "address": "", "channel": "", "lastname": "", "birthdate": "", "firstname": "", "created_at": "2021-10-20, 20:06", "postalcode": "", "updated_at": "2023-11-06, 17:47", "housenumber": "", "phonenumber": "", "is_anonymous": "False", "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=123", "a_custom_attribute": "", "loyalty_balance": "1612", "prepaid_balance": "20", "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Imx6TzJVV1ZwNlBRd3VkQmV1NnhKbnc9PSIsInZhbHVlIjoiVnlKZWM4OG5Vdjl0QmNEY242YmZ3OFNpWjQwQXkwRThmMCttWHlwQmNSTFluNzdpVkQwY0ZEcVo5TktvUXRHSVBYUE43S2dscFpyMUJnazFrWWZWVHI3cDNLTVJsZDcwWU9zQWdIejh5YjRQOGx4RjV6aDJjR3FTWlI1RTNlQlRrZEVNcURZeFlaZlYvQ2R2STdSU1RDdFdZT0RRdkxNN01vOXhLQnhaZkNVPSIsIm1hYyI6ImJjMzczNWQ2NGEwNzZlNjZhZTU0OTk3MzIxOTczODYzZjMxZjYwNzM2MjY5ZWJkODRkMTEzODU0NmQxMDU4OTQiLCJ0YWciOiIifQ==", "custom_app_login_code": "198169", "subscription_status_1": "Unsubscribed", "subscription_status_3": "Unsubscribed", "subscription_status_4": "Unsubscribed", "subscription_status_5": "Unsubscribed", "loyalty_associated_shops": "Krusty Krab Downtown and Sesame Street", "previous_loyalty_balance": "1617", "default_contact_identifier": "hash123456", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/123", "loyalty_last_transaction_date": "2023-11-06, 12:32", "loyalty_first_transaction_date": "2023-01-09, 16:03", "loyalty_number_of_transactions": "55", "loyalty_total_credits_received": "1697", "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=3" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Update Contact Contact's attributes. Please note that system values or system defined attributes, such as 'credit balance' or 'created at', cannot be updated. Example payload: ```json { "attributes": { "firstname": "John", "lastname": "Doe", "example_custom_attribute": "Hello", } } ``` ```http PUT https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID **Body** - `attributes` *array, required* — An array of the attributes to be updated. **Response Example** ```json { "data": { "id": 22, "uuid": "123", "email": "spongebob@bikinibottom.sea", "tier": { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "name": "Krusty Krew", "description": "Krusty Krew Members only", "position": 1, "media": null }, "subscriptions": [ { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Marketing", "description": "Subscribe here for marketing purposes", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": "123123123", "id": 1 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Bikini Bottom Deals", "description": "A monthly email blast with our best offers!", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": null, "id": 3 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Events", "description": "Stay in the loop for all our special events", "active": true, "strategy": "OPT_IN", "uuid": null, "id": 4 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Krusty Krab News", "description": "Never miss out on any of our newest features", "active": true, "strategy": "OPT_OUT", "uuid": null, "id": 5 } } ], "credit_balance": { "balance": 1612, }, "prepaid_balance": { "balance_in_cents": 2000 }, "current_values": { "age": null, "city": "Bikini Bottom", "dier": null, "tier": "1", "uuid": "123", "email": "spongebob@bikinibottom.sea", "avatar": "", "locale": null, "status": "ACTIVE", "address": "", "channel": null, "lastname": "", "birthdate": null, "firstname": "", "created_at": "2021-10-20T18:06:09+00:00", "postalcode": "9999YZ", "updated_at": "2023-11-06T16:47:00+00:00", "housenumber": 12, "phonenumber": "", "is_anonymous": false, "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=123", "a_custom_attribute": null, "loyalty_balance": 1612, "prepaid_balance": 20, "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Ikh3ZW8yRW9pSU9Gc25SYmxRUFVVWUE9PSIsInZhbHVlIjoianhuYncxVHBlK0dqMTN4RHhXNzdZNVJ3aGJqcERFdmVWQlcxYjFNZVZYSVdoWVVMWk54K1UvQ2lFek1nMmtGaTJZNGRNOG1rUUxwRGNnU25JcitpN0d5RWVKN21kS1gvSmMvNTA2bmxqU0Z0cmxoR2pPT0Ewc1hXeEJsMkVJNGFDQlNlSWpHRXVpWndyd3g3eTVXck90Z0tlL3J2RHNRKzRjRkNGb0tmVmVzPSIsIm1hYyI6ImZlMzc0ZDYzZDVjNTNjOTc3YTc1ZTMxN2RmMzM4MDFkZWQ2YWI2MDkwNDY4OWQ1MDk0ZWRkN2Y2ZTdhZjY5ZmEiLCJ0YWciOiIifQ==", "custom_app_login_code": 198169, "subscription_status_1": "UNSUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "loyalty_associated_shops": "123123;5fd3322e-671e-430f-aa38-5ed02e600ff7", "previous_loyalty_balance": 1617, "default_contact_identifier": "hash123456", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/123", "loyalty_last_transaction_date": "2023-11-06T11:32:00+00:00", "loyalty_first_transaction_date": "2023-01-09T15:03:37+00:00", "loyalty_number_of_transactions": 55, "loyalty_total_credits_received": 1697, "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=3" }, "current_display_values": { "age": "", "city": "", "dier": "False", "tier": "Gold", "uuid": "", "email": "spongebob@bikinibottom.sea", "avatar": "", "locale": "", "status": "Active", "address": "", "channel": "", "lastname": "", "birthdate": "", "firstname": "", "created_at": "2021-10-20, 20:06", "postalcode": "", "updated_at": "2023-11-06, 17:47", "housenumber": "", "phonenumber": "", "is_anonymous": "False", "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=123", "a_custom_attribute": "", "loyalty_balance": "1612", "prepaid_balance": "20", "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Imx6TzJVV1ZwNlBRd3VkQmV1NnhKbnc9PSIsInZhbHVlIjoiVnlKZWM4OG5Vdjl0QmNEY242YmZ3OFNpWjQwQXkwRThmMCttWHlwQmNSTFluNzdpVkQwY0ZEcVo5TktvUXRHSVBYUE43S2dscFpyMUJnazFrWWZWVHI3cDNLTVJsZDcwWU9zQWdIejh5YjRQOGx4RjV6aDJjR3FTWlI1RTNlQlRrZEVNcURZeFlaZlYvQ2R2STdSU1RDdFdZT0RRdkxNN01vOXhLQnhaZkNVPSIsIm1hYyI6ImJjMzczNWQ2NGEwNzZlNjZhZTU0OTk3MzIxOTczODYzZjMxZjYwNzM2MjY5ZWJkODRkMTEzODU0NmQxMDU4OTQiLCJ0YWciOiIifQ==", "custom_app_login_code": "198169", "subscription_status_1": "Unsubscribed", "subscription_status_3": "Unsubscribed", "subscription_status_4": "Unsubscribed", "subscription_status_5": "Unsubscribed", "loyalty_associated_shops": "Krusty Krab Downtown and Sesame Street", "previous_loyalty_balance": "1617", "default_contact_identifier": "hash123456", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/123", "loyalty_last_transaction_date": "2023-11-06, 12:32", "loyalty_first_transaction_date": "2023-01-09, 16:03", "loyalty_number_of_transactions": "55", "loyalty_total_credits_received": "1697", "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=123&subscription_type=3" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Delete Contact Deletes a Contact. This action is irreversible, the Contact's Credit Balance and any relation to other entities within the system will be removed, although the entities themselves may remain. When using 'DEFAULT' as the deletion type, a log is created containing detailed information. If 'GDPR' is used as the deletion type, any reference to personal data is removed. **Note:** This API call schedules the removal of the Contact, this may take a few minutes. ```http POST https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}}/delete ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID. **Body** - `type` *string, required* — The deletion type. Options are DEFAULT and GDPR. **Response Example** ```json { "data": null, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `55031` | Contact not found. | ## Claim Anonymous Contact This function enables the conversion of an anonymous Contact (registered with a fictional email address) into a recognized Contact with a real email address. If a Contact already exists with the provided real email address, the anonymous and existing Contacts will be merged. ```http PUT https://api.leat.com/api/v3/oauth/clients/contacts/{{anonymous_contact_uuid}}/claim ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `anonymous_contact_uuid` *string, optional* — The anonymous Contact's UUID that contains a fictional email address **Body** - `email` *string, optional* — The real email address for the Contact that is about to be not anonymous anymore. **Response Example** ```json { "data": { "id": 45842, "uuid": "999", "email": "patrick@bikinibottom.sea", "tier": null, "subscriptions": [ { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Marketing", "description": "Subscribe here for marketing purposes", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": "123123123", "id": 1 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Bikini Bottom Deals", "description": "A monthly email blast with our best offers!", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": null, "id": 3 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Events", "description": "Stay in the loop for all our special events", "active": true, "strategy": "OPT_IN", "uuid": null, "id": 4 } }, { "is_subscribed": false, "status": "UNSUBSCRIBED", "subscription_type": { "name": "Krusty Krab News", "description": "Never miss out on any of our newest features", "active": true, "strategy": "OPT_OUT", "uuid": null, "id": 5 } } ], "credit_balance": { "balance": 0, "id": 2625795 }, "prepaid_balance": { "balance_in_cents": 0 }, "current_values": { "age": null, "city": "Bikini Bottom", "dier": "false", "tier": null, "uuid": "3a685add-1711-425d-9184-fc6db18e0e1d", "email": "plankton@bikinibottom.sea", "avatar": "", "locale": null, "status": "ACTIVE", "address": "", "channel": "OAUTH_API", "lastname": "", "birthdate": null, "firstname": "", "created_at": "2023-11-01T14:59:14+00:00", "postalcode": "9999YZ", "updated_at": "2023-11-01T15:05:00+00:00", "housenumber": 12, "phonenumber": "", "is_anonymous": true, "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=3a685add-1711-425d-9184-fc6db18e0e1d", "a_custom_attribute": null, "loyalty_balance": 0, "prepaid_balance": 0, "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6Ikx2dDRqSTF4U3phV2s1PL1BIcG1STWc9PSIsInZhbHVlIjoiVnc0dUdhblAzZDdod29NQnhmVDlqbFdxYkF3S041WnhlN3hJQ3hrYklaa3BVVzhGbkIrM2R3citUWDVRQnRFL0IrSFJtY1JoQ09WRmR4N3hrZGFzdXBUL2pQVDF4aW9nWFEwQTFUcmliQk5EYXVBaEVpODZiMzh6Qm9Kb0E5QjZaOXM4bjQ3bFhIVEY3SVovRzRnejJZZlVYZUp0ZnA2RUNlUHVYYWlYNytucER5QndxYVNUSnRYVm9na1FrempOTDBmRFE0QkhJOFFIYTM2SnMvcTJ2QT09IiwibWFjIjoiN2JiZTQ5ZDVjMjgyMjY3NDkzYjE2ZTQ0ZTc5M2UyNjUyZWU3ODdiYmFlNzg2N2FjZjJjMDg3MDcxMGUzNTcxNSIsInRhZyI6IiJ9", "custom_app_login_code": 184014, "subscription_status_1": "UNSUBSCRIBED", "subscription_status_3": "UNSUBSCRIBED", "subscription_status_4": "UNSUBSCRIBED", "subscription_status_5": "UNSUBSCRIBED", "loyalty_associated_shops": "", "previous_loyalty_balance": 0, "default_contact_identifier": null, "subscription_preferences_url": "http://piggy.eu/subscription-preferences/3a685add-1711-425d-9184-fc6db18e0e1d", "loyalty_last_transaction_date": null, "loyalty_first_transaction_date": null, "loyalty_number_of_transactions": 0, "loyalty_total_credits_received": 0, "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=3a685add-1711-425d-9184-fc6db18e0e1d&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=3a685add-1711-425d-9184-fc6db18e0e1d&subscription_type=3" }, "current_display_values": { "age": "", "city": "", "dier": "False", "tier": "", "uuid": "", "email": "plankton@bikinibottom.sea", "avatar": "", "locale": "", "status": "Active", "address": "", "channel": "Oauth API", "lastname": "", "birthdate": "", "firstname": "", "created_at": "2023-11-01, 15:59", "postalcode": "", "updated_at": "2023-11-01, 16:05", "housenumber": "", "phonenumber": "", "is_anonymous": "True", "add_to_wallet": "https://leat.com/add-to-wallet/loyalty?uuid=3a685add-1711-425d-9184-fc6db18e0e1d", "a_custom_attribute": "", "loyalty_balance": "0", "prepaid_balance": "0", "custom_app_login_url": "https://leat.com/home?token=eyJpdiI6ImhWRlZWek8vUDBFWHpGUFRqaXVuQ1E9PSIsInZhbHVlIjoiRFU4WHF4bitIOFVuMHZvdEdFcjZYSlJOZFFxMHJ6bVpNVW4yZVRCZktna3ZXTExZb3d5RnRYNEVIU2FWM3RrMThOamZlUWRXRTdhdCtxazgvQjRyVEJrREwrNDFsSkhpUERWTTJLTTRkbDMrd1kzM3d5KzdLMXMvbVFZcGVFaHFSR0p4aG5WMUJlRHg0UFB6NllINTdYMURhMzMxY1FuVVZZeGdrcmRGaWIrQytxdXBveVZHampsbUdpUlowWktDbEpzbFlHTzBQSWFyWHo0N2ErbWFzZz09IiwibWFjIjoiOTQ4ZmExZGM2YWU4NTAwYjIxZTY4ZmI5YzhhZDFiYTQzMDQ5ZTdmNWNlNGQyNmU3Yjk3MmZkNDI5ZjJmYzE3NSIsInRhZyI6IiJ9", "custom_app_login_code": "184014", "subscription_status_1": "Unsubscribed", "subscription_status_3": "Unsubscribed", "subscription_status_4": "Unsubscribed", "subscription_status_5": "Unsubscribed", "loyalty_associated_shops": "", "previous_loyalty_balance": "0", "default_contact_identifier": "", "subscription_preferences_url": "http://piggy.eu/subscription-preferences/3a685add-1711-425d-9184-fc6db18e0e1d", "loyalty_last_transaction_date": "", "loyalty_first_transaction_date": "", "loyalty_number_of_transactions": "0", "loyalty_total_credits_received": "0", "double_opt_in_confirmation_url_1": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=3a685add-1711-425d-9184-fc6db18e0e1d&subscription_type=1", "double_opt_in_confirmation_url_3": "http://piggy.eu/double-opt-in-confirmation?contact_uuid=3a685add-1711-425d-9184-fc6db18e0e1d&subscription_type=3" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `60016` | Contact not anonymous. | ## Merge Contact > **Warning** > > WARNING: Use at your own risk and with consent from your clients. Before using, **ensure both email addresses belong to the same person**. Merges a 'source' Contact into a 'destination' Contact. The 'destination' Contact will be the resulting Contact, whilst the 'source' Contact will be removed. **Note:** This cannot be undone, so use with care. Only use after verifying the person in question has access to both email addresses. **Note:** This API call schedules the merge of the Contacts, this may take a few minutes. ```http POST https://api.leat.com/api/v3/oauth/clients/contacts/merge ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `source_contact_uuid` *string, required* — The UUID of the Source Contact which will cease to exist. - `destination_contact_uuid` *string, required* — The UUID of the Destination Contact which remain. **Possible errors** | Code | Message | | --- | --- | | `55031` | Contact not found. | ## Get Contact Identifiers Use this endpoint to retrieve a Contact's Contact Identifiers. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}}/contact-identifiers ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID from whom you want to retrieve the Contact Identifiers. **Response Example** ```json { "data": { [ "name": "Main Loyalty Card", "value": "ABCDE12356-90", "active": true, "default": true, "contact": { "uuid": "123abc-def789-qwerty-34567", } ], [ "name": "Gary Card", "value": "QWERTY-7890", "active": true, "default": false, "contact": { "uuid": "123abc-def789-qwerty-34567", } ], }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Get Credit Balance Retrieve the current Credit Balance of a specific Contact, identified through their UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}}/credit-balance ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID **Response Example** ```json { "data": { "balance": 49 }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Get Prepaid Balance Use this function to retrieve the Prepaid Balance of a particular Contact, identified by their UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}}/prepaid-balance ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID **Response Example** ```json { "data": { "balance_in_cents": 5000, "balance": "€50.00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Get Tier Use this endpoint to retrieve the current loyalty Tier of a specific Contact. It’s useful for understanding a Contact's loyalty status and offering them tailored Rewards based on their Tier. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}}/tier ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID from whom you want to retrieve the Tier. **Response Example** ```json { "data": { "name": "Krusty Krew", "description": "Krusty Krew Members only", "position": 1, "media": null, "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Related - [Contact Identifiers](https://docs.leat.com/v3/oauth/contact-identifiers.md) — Contacts can also be found using Contact Identifiers - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/contacts --- # Contacts Portal Each Leat Client has their own Contacts Portal, which is basically a web app which they can use to serve their Contacts. The Contacts Portal allows you to create your own platform for your Loyalty Program. You can personalize this environment to fit the look and feel of your business. ## Get Authenticated Url This function generates a secure, authenticated URL specifically for a given Contact, identified by their UUID. This URL grants the Contact access to the portal, ensuring a safe and personalized experience. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts-portal/auth-url ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The UUID of the Contact for whom the authenticated url is to be generated. **Response Example** ```json { "data": { "url": "https://custom.appurl.com/home?token=eyJpdiI6IkF3NXpyM1FGcls5VW5MQjZUR0FPMVE9PSIsInZhbHVlIjoiYUFa2HFVVFdmTmQ2aGdOcmdyQUFMTjJvSFAyS2R2RHdSZDFscC84Mnd4V0NrSTdMbTJZaDhrZUN5TENjKzJYd0ZmYXpybWwvVFVyRXlwVGRvWTdEcHc9PSIsIm1hYyI6IjkwNzdlZDU3ZjIyMGIyOGVmM2NiYzVlOWM3ZGViYWIwMjAzYjNkYTc0M2Y1NGYyYmYyZDFlMWMyZjE2MzJlOGEiLCJ0YWciOiIifQ==" }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Related - [Contact Identifiers](https://docs.leat.com/v3/oauth/contact-identifiers.md) — Contacts can also be found using Contact Identifiers - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/contacts-portal --- # Credit Receptions > **Warning** > > Credit Receptions have been largely replaced by top level entities such as [Orders](https://docs.leat.com/v3/oauth/orders.md), [Visits](https://docs.leat.com/v3/oauth/visits.md) and [Bookings](https://docs.leat.com/v3/oauth/bookings.md). Please use those instead. If your use case doesn't fit any of these, please contact us at [dev@leat.com](mailto:dev@leat.com) to discuss. Credit Receptions are instances of loyalty points transactions in the system. It lets our end-users collect credits and it's therefore the basis for virtually every loyalty integration. Before starting to build your integration, we ask you read the recommended flows. ## Refunds and reversing Credit Receptions At times, initially issued credits need to be reverted, when an order was refunded for instance. In such a case, it's important to note that creating a Credit Reception with a negative `unit_value` is inadvisable. Due to the possibility of Loyalty Rules dependent on variables (e.g. time-based filters or Tier-based filters) being present, the resulting number of credits issued can differ from the initial transaction to be corrected. **Instead, use the `Reverse Credit Reception` API call to reverse an initial transaction.** ### Partial refunds In case of partial refunds, it gets a bit more tricky. To account for time-based and attribute-based filters as best as possible, we recommend to calculate the percentage of the order that was refunded, take that percentage of the *credits* initially issued, and create a Credit Reception with that negative amount of `credits` (do *not* use`unit_value` (e.g. initial transaction of €50.00 issued 80 credits, €20.00 was refunded, so new Credit Reception for -32 credits). ## Create Credit Reception In creating a Credit Reception, you may either choose to set the `unit_value` alongside the relevant `unit_name`, or the `credits`, or both. In any case, one of either must be set. If only purchase amount is set, then the number of credits are determined by any Loyalty Rules set on the Loyalty Program. Loyalty Rules are rules that Clients can set in their Business Dashboard, which can apply multiplication or rounding rules for instance. To mimic in-store behaviour as closely as possible, we recommend setting `unit_name`and `unit_value`only. If `unit_name`is not set, then the default unit will be used (purchase\_amount). If, however, the number of `credits`to be given should be set explicitly - for instance when the Credit Reception doesn't concern a transaction or a (partial) refund needs to be made - then the credits can be set. In this case, `unit_value`is optional. > **Warning** > > When using purchase amount as unit\_name, use purchase amount as a float for unit\_value (e.g. €10,40 translates to unit\_value: 10.40). ```http POST https://api.leat.com/api/v3/oauth/clients/credit-receptions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `shop_uuid` *string, required* — The UUID of the Shop you want to create the Reward Reception for. - `contact_uuid` *string, required* — The UUID of the Contact for whom the Credit Reception is to be created. - `credits` *number, required_if* — Number of Credits, required if unit_value is not set. Overrides any Loyalty Rules. - `unit_value` *number, required_if* — Value of the Unit to be given. This parameter is required if credits is not set. Subject to any Loyalty Rules. - `unit_name` *string, optional* — Name of the Unit for the corresponding value. If both unit_name and credits are not set, then the default unit (purchase_amount) is used. - `contact_identifier_value` *string, optional* — The value of the Contact Identifier linked to the Contact for whom the Credit Reception is to be created. - `pos_transaction_uuid` *string, optional* — The UUID of the POS transaction that you want to link the Credit Reception to. **Response Example** ```json { "data": { "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 1642 } }, "shop": { "uuid": "abc2-3456-ask03l-9dflas", "type": "physical", "name": "Krusty Krab", "address": { "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "credits": 10, "created_at": "2023-11-06T09:12:32+00:00", "uuid": "294183c2-20f2-4f86-a5e5-17e71c4b0660", "unit": { "name": "purchase_amount", "label": "Purchase amount", "prefix": "€", "is_default": true }, "unit_value": null, }, "meta": {} } } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `1008` | Shop not found. | | `55031` | Contact not found. | | `60003` | Contact identifier not found. | | `60007` | Unit not found. | | `1015` | Maximum amount of credits reached. | | `60001` | Credit reception creating in progress. | | `55046` | No default unit found. | | `1027` | POS Transaction not found. | | `6001` | The POS transaction is already linked to a credit reception. | ## Reverse Credit Reception At times, an initial Credit Reception needs to be reversed, because of a refund for instance. This will create a new Credit Reception with the same data as initially used, but for the negative number of credits issued. ```http POST https://api.leat.com/api/v3/oauth/clients/credit-receptions/{{credit_reception_uuid}}/reverse ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `credit_reception_uuid` *string, required* — The UUID of the Credit Reception that needs to be reversed **Response Example** ```json { "data": { "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 1642 } }, "shop": { "uuid": "abc2-3456-ask03l-9dflas", "type": "physical", "name": "Krusty Krab", "address": { "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "credits": 10, "created_at": "2023-11-06T09:12:32+00:00", "uuid": "294183c2-20f2-4f86-a5e5-17e71c4b0660", "unit": { "name": "purchase_amount", "label": "Purchase amount", "prefix": "€", "is_default": true }, "unit_value": null, }, "meta": {} } } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Calculate Credits Without actually creating a Credit Reception, this API call can be used to calculate how many credits would be issued if a Credit Reception was to be made with the given values. Please note, that there is a (small) possibility of a divergence in credits between the calculation and the actual Credit Reception, if loyalty rule filters are in place that are time-based (e.g. a happy hour rule). ```http GET https://api.leat.com/api/v3/oauth/clients/credit-receptions/calculate ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `shop_uuid` *string, required* — The UUID of the Shop you want to create the Reward Reception for. - `contact_uuid` *string, optional* — The UUID of the Contact for whom the credits are to be calculated. - `unit_value` *number, required* — Value for which credits are to be calculated. Subject to any Loyalty Rules. - `unit_name` *string, optional* — Name of the unit for the corresponding value. If both unit_name and credits are not set, then the default unit (purchase_amount) is used. **Response Example** ```json { "data": { "credits": 10240 }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `1008` | Shop not found. | | `55031` | Contact not found. | | `60007` | Unit not found. | ## Related - [Loyalty Tokens](https://docs.leat.com/v3/oauth/loyalty-tokens.md) — Create or Claim a Loyalty Token - [Reward Receptions](https://docs.leat.com/v3/oauth/reward-receptions.md) — It's what Contacts are saving up for: Reward Receptions Source: https://docs.leat.com/v3/oauth/credit-receptions --- # Custom Attribute Groups ## List Custom Attribute Groups Retrieve all Custom Attribute Groups for a specific entity. ```http GET https://api.leat.com/api/v3/oauth/clients/custom-attribute-groups ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `entity` *string, required* — Entity for which Custom Attribute Groups are to be retrieved Possible values: - `business_profiles` — For entity: Business Profile (or Shop) - `contact` — For entity: Contact - `reward` — For entity: Reward - `promotion` — For entity: Promotion (note: use this one when working with Vouchers) - `voucher` — For entity: Voucher (note: this is for individual vouchers, you'll likely need Promotion instead - `giftcard` — For entity: Giftcard - `giftcard_transaction` — For entity: Giftcard Transaction - `prepaid` — For entity: Prepaid Transaction - `booking` — For entity: Booking - `visit` — For entity: Visit - `shift` — For entity: Shift **Response Example** ```json { "data": [ { "entity": "promotion", "name": "acme_integration", "label": "Acme Integration Attributes", "created_at": "2023-11-13T16:04:14+00:00", }, { "entity": "promotion", "name": "general", "label": "General", "created_at": "2023-10-12T14:32:15+00:00", }, ], "meta": [] } ``` ## Create Custom Attribute Group Custom Attributes can be grouped, which can be helpful to identify those attributes that are associated to your integration. ```http POST https://api.leat.com/api/v3/oauth/clients/custom-attribute-groups ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `name` *string, required* — A unique internal identifier for this group - `label` *string, optional* — Display value for this group - `entity` *string, required* — Entity for which this Custom Attribute Group is to be created Possible values: - `business_profiles` — For entity: Business Profile (or Shop) - `contact` — For entity: Contact - `reward` — For entity: Reward - `promotion` — For entity: Promotion (note: use this one when working with Vouchers) - `voucher` — For entity: Voucher (note: this is for individual vouchers, you'll likely need Promotion instead - `giftcard` — For entity: Giftcard - `giftcard_transaction` — For entity: Giftcard Transaction - `prepaid` — For entity: Prepaid Transaction - `booking` — For entity: Booking - `visit` — For entity: Visit - `shift` — For entity: Shift **Response Example** ```json { "data": { "entity": "promotion", "name": "acme_integration", "label": "Acme Integration Attributes", "created_at": "2023-11-13T16:04:14+00:00", }, "meta": [] } ``` ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/custom-attribute-groups --- # Custom Attributes Custom Attributes are flexible, tailored attributes that allow you to extend the entities in the Piggy system. Currently, Custom Attributes can be created for the following entities: `business_profiles`For entity: Business Profile (or Shop) `contact`For entity: Contact `reward`For entity: Reward `promotion`For entity: Promotion (note: use this one when working with Vouchers) `voucher`For entity: Voucher (note: this is for individual vouchers, you'll likely need Promotion instead `giftcard`For entity: Giftcard `giftcard_transaction`For entity: Giftcard Transaction `prepaid`For entity: Prepaid Transaction `booking`For entity: Booking `visit`For entity: Visit `shift`For entity: Shift If you're missing any entity here, please reach out so we can see if we can make it happen on short notice. ## List Custom Attributes Get a list of Custom Attributes for a specified entity. ```http GET https://api.leat.com/api/v3/oauth/clients/custom-attributes ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `entity` *string, required* — The entity of the Custom Attributes you want to retrieve. **Response Example** ```json { "data": [ { "entity": "voucher", "name": "acme_promotion_id", "label": "Acme Promotion ID", "type": "text", "group_name": null, "group": { "name": "General", "created_by_user": null, "position": 1 }, "is_piggy_defined": false, "is_soft_read_only": false, "is_hard_read_only": false, "has_unique_value": false, "description": "Promotion for Acme discount logic", "options": null, "position": 1, "created_at": "2023-11-13T16:04:14+00:00", "meta": [], } ] } ``` ## Create Custom Attribute Creates a new Custom Attribute for a specified entity. You need to provide essential details like the entity type, name, label, and data type of the attribute. There's also the option to add a description and, for select attributes, an array of options. ```http POST https://api.leat.com/api/v3/oauth/clients/custom-attributes ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `entity` *string, required* — The entity that you want to create the Custom Attributes for. - `name` *string, required* — The unique value for the Custom Attribute, used for internal purposes. - `label` *string, required* — The label for the attribute, which is outputted. - `type` *string, required* — The data type of the attribute, possible options: url, text, date, phone, float, color, email, number, select, boolean, rich_text, date_time, long_text, date_range, time_range, identifier, birth_date, file_upload, media_upload, multi_select or license_plate. - `options` *array, optional* — Applicable for (multi)selects only. Array consisting of name and value arrays. - `description` *string, optional* — A description for the attribute. - `group_name` *string, optional* — Name of the Group to which the attribute belongs. **Response Example** ```json { "data": { "entity": "promotion", "name": "discount_type", "label": "Discount Type", "type": "select", "group": { "name": "General", }, "is_piggy_defined": false, "is_soft_read_only": false, "is_hard_read_only": false, "has_unique_value": false, "description": "Type of discount for this Voucher", "options": [ { "value": "percentage", "label": "Percentage", }, { "value": "fixed", "label": "Fixed", }, ], "created_at": "2023-11-13T16:04:14+00:00", }, "meta": [] } ``` ## Update Custom Attribute Updates the properties of a Custom Attribute. Note that only the `label`, `description`, `options` for selects and multiselects, `group` can be updated. Attributes defined by the system cannot be updated. ```http PUT https://api.leat.com/api/v3/oauth/clients/custom-attributes/{{custom_attribute_name}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `entity` *string, required* — The entity that you want to create the Custom Attributes for. - `label` *string, optional* — The label for the attribute, which is outputted. - `options` *array, optional* — Applicable for (multi)selects only. Array consisting of name and value arrays. - `description` *string, optional* — A description for the attribute. - `group_name` *string, optional* — Name of the Group to which the attribute belongs. **Response Example** ```json { "data": { "entity": "promotion", "name": "discount_type", "label": "Discount Type", "type": "select", "group": { "name": "General", }, "is_piggy_defined": false, "is_soft_read_only": false, "is_hard_read_only": false, "has_unique_value": false, "description": "Type of discount for this Voucher", "options": [ { "value": "percentage", "label": "Percentage", }, { "value": "fixed", "label": "Fixed", }, ], "created_at": "2023-11-13T16:04:14+00:00", }, "meta": [] } ``` ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/custom-attributes --- # Forms Forms are a simple yet powerful tool for collecting valuable information about your customers or employees. They allow you to systematically gather data, which can be easily transformed into meaningful business insights. Whether you're looking to understand customer spending habits or expand profiles, Forms offer the flexibility to tailor questions to your specific needs. ## List Forms Retrieve of all Forms linked to an Account. It's possible to filter both on`status`and `type` ```http GET https://api.leat.com/api/v3/oauth/clients/forms ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `status` *string, optional* — Status of Forms to be retrieved, possible values: PUBLISHED, DRAFT, BIN. - `type` *string, optional* — Type of Forms to be retrieved, possible values: PUBLIC, PRIVATE. **Response Example** ```json { "data": [ { "uuid": "a4fb2960-ef15-43da-96e0-18aa83861ace", "name": "mikes", "status": "PUBLISHED", "type": "PUBLIC", "url": "http://piggy.eu/forms/a4fb2960-ef15-43da-96e0-18aa83861ace" } ], "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Related - [Contact Identifiers](https://docs.leat.com/v3/oauth/contact-identifiers.md) — Contacts can also be found using Contact Identifiers - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/forms --- # Giftcard Programs The Piggy Giftcards system has two types of giftcards: physical and digital giftcards, which are tied to a specific Giftcard Program. Each program includes details like `card_count`, status, and `expiration_days`. For a more detailed description of what comprises a Giftcard, please checkout the Giftcard section. ## List Giftcard Programs Retrieve a list of your Giftcard Programs. ```http GET https://api.leat.com/api/v3/oauth/clients/giftcard-programs ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "id": 1, "card_count": 29928, "settles": 0, "uuid": "123", "name": "Bikini Bottom Bucks", "active": true, "expiration_days": 394, "max_amount_in_cents": 51000, "min_amount_in_cents": null, "calculator_flow": "DEFAULT" }, { "id": 54, "card_count": 81, "settles": 0, "uuid": "8fb59dcf-fb5b-4cee-88d8-35e361e55a12", "name": "asdas", "active": true, "expiration_days": null, "max_amount_in_cents": null, "min_amount_in_cents": null, "calculator_flow": "DEFAULT" } ], "meta": [] } ``` ## Related - [Giftcard Transactions](https://docs.leat.com/v3/oauth/giftcard-transactions.md) — Use giftcards as a payment method using Giftcard Transactions - [Prepaid Transactions](https://docs.leat.com/v3/oauth/prepaid-transactions.md) — A new form of giftcards, now contact-based Source: https://docs.leat.com/v3/oauth/giftcard-programs --- # Giftcard Transactions Our software only allows non-reincrementable Giftcards for transaction creation through OAuth, due to security reasons. This ensures that Giftcards cannot have negative balances; in other words, they cannot be decremented by an amount larger than their current stored value. Each transaction is linked to a Giftcard, represented by a hash or QR-code and an ID, offering secure and reliable tracking of Giftcard usage. ## List Giftcard Transactions This API call provides a list of all Giftcard Transactions. It offers filtering options based on per-page count, page number, Giftcard hash, Giftcard Program UUID, and Shop UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/giftcard-transactions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `perPage` *number, optional* — Limit of records to retrieve. Default: 10; Maximum: 100. - `page` *number, optional* — Page to retrieve. Default: 1. - `giftcard_hash` *string, optional* — Retrieve transactions for a specific giftcard - `giftcard_program_uuid` *string, optional* — Retrieve transactions for a specific giftcard program - `shop_uuid` *string, optional* — Retrieve transactions performed at a specific Shop - `sort` *string, optional* — Sort by column. Ascending by default, prefix with a hyphen to sort descending (e.g. ?sort=-created_at) Possible values: - `created_at` — 'Sort by creation date. Prefix with '-' for descending. - `created_at` *array, optional* — Filter on creation date, can only be sent in as array. Date value must be in ISO8601 format. Combine to retrieve transactions between two dates. Possible values: - `created_at[gt]` — Filter on creation date is greater than value. - `created_at[gte]` — Filter on creation date is greater than or equal to value. - `created_at[lt]` — Filter on creation date is lower than value. - `created_at[lte]` — Filter on creation date is lower than or equal to value. **Response Example** ```json { "data": [ { "id": 1, "uuid": null, "amount_in_cents": 2500, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 1397, "uuid": "37485734857385", "name": "The Chum Bucket" }, "settlements": [], "created_at": "2016-11-29T18:42:34+00:00" }, { "id": 2, "uuid": null, "amount_in_cents": -2200, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 1397, "uuid": "37485734857385", "name": "The Chum Bucket" }, "settlements": [], "created_at": "2016-11-29T18:43:13+00:00" }, { "id": 3, "uuid": null, "amount_in_cents": 1000, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 1397, "uuid": "37485734857385", "name": "The Chum Bucket" }, "settlements": [], "created_at": "2016-11-30T09:25:26+00:00" }, { "id": 4, "uuid": null, "amount_in_cents": -500, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 1397, "uuid": "37485734857385", "name": "The Chum Bucket" }, "settlements": [], "created_at": "2016-11-30T09:25:43+00:00" }, { "id": 5, "uuid": null, "amount_in_cents": -800, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 1397, "uuid": "37485734857385", "name": "The Chum Bucket" }, "settlements": [], "created_at": "2016-11-30T09:26:08+00:00" }, { "id": 6, "uuid": null, "amount_in_cents": 10, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 161, "uuid": "12312312312312", "name": "Chum Bucket Online" }, "settlements": [], "created_at": "2016-11-30T12:48:00+00:00" }, { "id": 7, "uuid": null, "amount_in_cents": -9, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 161, "uuid": "12312312312312", "name": "Chum Bucket Online" }, "settlements": [], "created_at": "2016-11-30T12:48:22+00:00" }, { "id": 8, "uuid": null, "amount_in_cents": -1, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 161, "uuid": "12312312312312", "name": "Chum Bucket Online" }, "settlements": [], "created_at": "2016-11-30T12:48:36+00:00" }, { "id": 9, "uuid": null, "amount_in_cents": 2000, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 161, "uuid": "12312312312312", "name": "Chum Bucket Online" }, "settlements": [], "created_at": "2016-11-30T13:16:18+00:00" }, { "id": 10, "uuid": null, "amount_in_cents": -500, "type": "STANDARD", "settled": false, "card": { "id": 1, "uuid": null }, "shop": { "id": 161, "uuid": "12312312312312", "name": "Chum Bucket Online" }, "settlements": [], "created_at": "2016-11-30T13:16:36+00:00" } ], "meta": { "page": 1, "limit": 10, "viewing_from": 1, "viewing_to": 10, "last_page": 120, "total": 1192 } } ``` ## Create Giftcard Transaction This API call facilitates the creation of a transaction for a specific Giftcard. It handles both increment and decrement transactions. A negative amount will trigger a decrementation, adjusting the Giftcard's balance accordingly. ```http POST https://api.leat.com/api/v3/oauth/clients/giftcard-transactions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `giftcard_uuid` *string, required* — The UUID of the Giftcard you want to create the transaction for. - `amount_in_cents` *integer, required* — The amount in cents with which the Giftcard is to be incremented or decremented. Supply a negative amount for a decrementation. - `shop_uuid` *string, required* — UUID of the Shop where the Giftcard Transaction has taken place. - `custom_attributes` *array, optional* — Additional Custom Attributes as a key-value array. **Response Example** ```json { "data": { "id": 2271, "uuid": "355892c5-6bf7-4167-bae2-3bf2bf44c3dc", "amount_in_cents": -1000, "type": "STANDARD", "settled": false, "card": { "id": 41650, "uuid": "45d7ce10-16cc-4bb7-b37f-e8c4bc553fdf" }, "shop": { "id": 15, "uuid": "123123", "name": "Krusty Krab" }, "settlements": [], "created_at": "2023-11-12T16:10:15+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `7010` | That giftcard is not valid. | | `5006` | Deze giftcard kan niet opgewaardeerd worden. | This giftcard cannot be incremented. | | `5008` | Maximale limiet op kaart bereikt. | Max amount will be exceeded. | | `5003` | Het bedrag op de kaart is niet toereikend. | Insufficient amount on giftcard. | | `5002` | Het bedrag op de kaart is verlopen. | This giftcard has expired. | | `1000` | Interne fout. | Internal error. | ## Get Giftcard Transaction Retrieve details of a specific Giftcard Transaction. By providing the transaction's UUID, you can access comprehensive information about individual transactions, allowing you to see and keep track of Giftcard Transactions, making it easier to manage Giftcards. ```http GET https://api.leat.com/api/v3/oauth/clients/giftcard-transactions/{{giftcard_transaction_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `giftcard_transaction_uuid` *string, required* — The UUID of the Giftcard Transaction that you want to retrieve. **Response Example** ```json { "data": { "id": 2268, "uuid": "9e6426ef-1916-4f3c-a94f-4cf6af4f945b", "amount_in_cents": 5, "type": "STANDARD", "settled": false, "card": { "id": 41592, "uuid": "f12e8cd7-47de-4f6d-bdf3-e282ace87eb0" }, "shop": { "id": 15, "uuid": "123123", "name": "Krusty Krab" }, "settlements": [], "created_at": "2023-10-09T10:02:43+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `5010` | Giftcard transaction not found. | ## Reverse Giftcard Transaction At times, an initial Giftcard Transaction needs to be reversed, because of a refund for instance. This will create a new Giftcard Transaction with the same data as initially used, but for the negative amount issued. ```http POST https://api.leat.com/api/v3/oauth/clients/giftcard-transactions/{{giftcard_transaction_uuid}}/reverse ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `giftcard_transaction_uuid` *string, required* — The UUID of the Giftcard Transaction that is to be reversed. **Response Example** ```json { "data": { "id": 2271, "uuid": "355892c5-6bf7-4167-bae2-3bf2bf44c3dc", "amount_in_cents": -1000, "type": "STANDARD", "settled": false, "card": { "id": 41650, "uuid": "45d7ce10-16cc-4bb7-b37f-e8c4bc553fdf" }, "shop": { "id": 15, "uuid": "123123", "name": "Krusty Krab" }, "settlements": [], "created_at": "2023-11-12T16:10:15+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `5010` | Giftcard transaction not found. | | `5006` | Deze giftcard kan niet opgewaardeerd worden. | This giftcard cannot be incremented. | | `5008` | Maximale limiet op kaart bereikt. | Max amount will be exceeded. | | `5003` | Het bedrag op de kaart is niet toereikend. | Insufficient amount on giftcard. | | `5002` | Het bedrag op de kaart is verlopen. | This giftcard has expired. | | `55025` | This is not the giftcards last transaction. | | `1000` | Interne fout. | Internal error. | ## Related - [Prepaid Transactions](https://docs.leat.com/v3/oauth/prepaid-transactions.md) — A new form of giftcards, now contact-based - [Giftcards](https://docs.leat.com/v3/oauth/giftcards.md) — Retrieve all the information stored on Giftcards Source: https://docs.leat.com/v3/oauth/giftcard-transactions --- # Giftcards Our Giftcards system has two types of Giftcards: physical and digital Giftcards. Each Giftcard is uniquely identified by a hash or QR-code, along with an ID. A Giftcard Program can hold any number of Giftcards. ## List Giftcards Returns a paginated list of Giftcards. ```http GET https://api.leat.com/api/v3/oauth/clients/giftcards ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `giftcard_program_uuid` *string, optional* — The UUID of the Giftcard Program for which you want to retrieve Giftcards. - `page` *integer, optional* — The page number. - `limit` *integer, optional* — The maximum number of Giftcards to retrieve (default: 30, max: 100). **Response Example** ```json { "data": [ { "uuid": "1646fa16-09f7-43d4-a361-a8660208485c", "type": "DIGITAL", "hash": "PIGGY123", "expiration_date": null, "active": true, "upgradeable": true, "amount_in_cents": 1500, "giftcard_program": { "uuid": "a203bee9-2523-4a19-8b7e-2d282d272b6f", "name": "Bikini Bottom Bucks" } }, { "uuid": "3a112128-0bf9-43c1-a154-e782a6ad9659", "type": "DIGITAL", "hash": "PIGGY345", "expiration_date": null, "active": true, "upgradeable": true, "amount_in_cents": 2500, "giftcard_program": { "uuid": "f6be82ff-bb19-46fa-ab5b-b55b03332bbe", "name": "Bikini Bottom Bucks" } }, { "uuid": "4a83975d-91a4-4e27-ada7-5d16bf5b68fa", "type": "DIGITAL", "hash": "PIGGY789", "expiration_date": null, "active": true, "upgradeable": true, "amount_in_cents": 5000, "giftcard_program": { "uuid": "77bde4c4-4aa1-4766-a498-3e291c41bddb", "name": "Bikini Bottom Bucks" } } ], "meta": { "page": 1, "per_page": 30, "last_page": 10, "total": 300 } } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `1100` | Giftcard Program not found. | ## Get Giftcard Finds a Giftcard by its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/giftcards/{{giftcard_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "uuid": "d694e2a2-e982-4bfc-8269-e79dc98b38e4", "type": "DIGITAL", "hash": "GFTCRD123", "expiration_date": null, "active": true, "upgradeable": true, "amount_in_cents": 2500, "display_amount": "€ 25,00", "giftcard_program": { "name": "Sea Dollars" } }, } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `5001` | Giftcard not found. | ## Find Giftcard by Hash Returns all relevant information for a Giftcard. Required for a create Giftcard Transaction call, as it provides all the necessary details. ```http GET https://api.leat.com/api/v3/oauth/clients/giftcards/find-one-by ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `hash` *string, required* — The Giftcard's unique hash or QR-code. **Response Example** ```json { "data": { "uuid": "d694e2a2-e982-4bfc-8269-e79dc98b38e4", "type": "DIGITAL", "hash": "GFTCRD123", "expiration_date": null, "active": true, "upgradeable": true, "amount_in_cents": 2500, "display_amount": "€ 25,00", "giftcard_program": { "name": "Sea Dollars" } }, } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `5001` | Giftcard not found. | ## Create Giftcard This API call allows creation of Digital Giftcards, as the creation of Physical Giftcards is currently not supported. To create a Digital Giftcard, use `type: 1` in your request. ```http POST https://api.leat.com/api/v3/oauth/clients/giftcards ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `type` *number, required* — Giftcard type - 0: Physical, 1: Digital. Only the creation of Digital Giftcards is supported at this time. - `giftcard_program_uuid` *string, required* — The UUID of the Giftcard Program to which the newly created Giftcard is to be linked. **Response Example** ```json { "data": { "id": 41654, "uuid": "5b3dab1c-bdde-4313-a74d-67de815c751c", "type": "DIGITAL", "hash": "41654", "amount_in_cents": 0, "upgradeable": true, "active": true, "expiration_date": "2024-12-10T16:26:20+00:00", "amount": 0, "url": "https://leat.com/giftcard-balance?hash=41654", "display_amount": "€0.00", "formatted_expiration_date": "2024-12-10", "giftcard_program": { "id": 1, "name": "Bikini Bottom Bucks", "uuid": "123", "max_amount_in_cents": 51000, "calculator_flow": "DEFAULT", "expiration_days": 394 }, "giftcard_recipients": [] }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55021` | Only Digital giftcards can by created using the public API. | | `1100` | Giftcard Program not found. | ## Send Giftcard by Email This API call sends out an email containing giftcard information. The email is to be preconfigured in the Account and adhere to the following requirements: (1) must be a valid Automated email, (2) must be of category 'Giftcard recipient', (3) subscription type must be configured and Contact must be subscribed to said subscription type and (4) content must contain 'giftcard.hash' personalisation token. The email to be sent can be specified. If not specified, the system will attempt to find an email which adheres to aforementioned requirements. If none can be found, the request will fail. ```http POST https://api.leat.com/api/v3/oauth/clients/giftcards/{{uuid}}/send-by-email ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_uuid` *string, required* — The Contact to whom the giftcard email is to be sent. - `email_uuid` *string, optional* — The email that is to be used to send the giftcard. If none is given, the system will attempt to use any valid email in the account. Email must contain the 'giftcard.hash' personalisation token. - `merge_tags` *array, optional* — An associative array of custom merge tags, to replace those in the email. Keys must start with 'custom.' (Example: custom.message: 'Hello') **Response Example** ```json { "data": { "message": "success", "data": "Email successfully sent", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `5001` | Giftcard not found. | ## Related - [Giftcard Transactions](https://docs.leat.com/v3/oauth/giftcard-transactions.md) — Use giftcards as a payment method using Giftcard Transactions - [Prepaid Transactions](https://docs.leat.com/v3/oauth/prepaid-transactions.md) — A new form of giftcards, now contact-based Source: https://docs.leat.com/v3/oauth/giftcards --- # Loyalty Program Contacts save credits at Loyalty Programs. Clients (people, store-owners or corporations who buy our software) may not use Loyalty at all, or join a Loyalty Program not belonging to them, or they may use several Loyalty Programs. The collection of credits by the end-user is Loyalty Program specific, meaning the credits saved at program A cannot be redeemed at program B. However, although a Loyalty Program belongs to one Account, that Account may choose to invite other accounts to partake in that Loyalty Program. The Loyalty Program has some global settings. Which can be retrieved with the get Loyalty Program call. ## Get Loyalty Program This method allows you to retrieve detailed information about a specific Loyalty Program. It provides key details such as the program's ID, `name`, maximum credit amount, `custom_credit_name`, and `unit`settings. This API call is allows for understanding the details of the Loyalty Program for your Account. ```http GET https://api.leat.com/api/v3/oauth/clients/loyalty-program ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "id": 3545, "name": "Squidward Test", "max_amount": 50000, "custom_credit_name": "Credits", "unit": { "name": "test", "label": "Calories", "prefix": "" } }, "meta": {} } ``` Source: https://docs.leat.com/v3/oauth/loyalty-program --- # Loyalty Tokens A Loyalty Token is, simply put, a Credit Reception not yet assigned to a specific Contact. The Contact claims the credits reserved by the Loyalty Token through a link. Until that time, no Credit Reception is created. You can create Loyalty Tokens in the Business Dashboard, request one with [Create Loyalty Token](#create-loyalty-token), or build the link yourself without calling the API, as described below. A link can be claimed once. The Contact opens it, signs in to the Contacts Portal and the credits are added to their balance. The `unique_id` identifies the link, so it must be unique within your loyalty program: a link that reuses a `unique_id` resolves to the earlier token rather than creating a new one. ## Creating a Loyalty Token A Loyalty Token is simply a URL, generated using a set of rules. This URL can be created without any API calls, as the rules are predefined. When the URL is visited - i.e. when the token is claimed - the backend decodes the URL, validates the token and allocates the credits to the Contact in question. You can either choose to directly set `credits` or to set the `unit_value` instead. In this latter case, the loyalty rules will be applied (e.g. double points for Gold members). Creating the Loyalty Token changes some of the parameters, so please choose whether you want to create a Loyalty Token using unit value or credits: ### Step 1: Generating the initial parameter string In order to create a Loyalty token, the following parameters are required: - `version` A version number indicating which token version you are using. The current version is **v3**. - `shop_uuid` The shop UUID of the shop that is providing the credits. - `unique_id` A unique id generated by the device/client that is creating the token (e.g. the order ID). It must be unique within your loyalty program. - `timestamp` A unix timestamp (this indicates the creation date of the token; it is not used to expire the link). - `unit_name` Name of the unit for the corresponding value. Default if unsure: `purchase_amount` - `unit_value` The value for which points are to be issued (e.g. the order amount). - `secret` The secret of the client’s Account, found under [Account secret](https://business.leat.com/apps/integrations/account-secret) in the Business Dashboard. Use the parameters above to construct the initial parameter string. To illustrate, we’ll be using the following dummy data: ```yaml version: v3 shop_uuid: abcde-1235-hijkl-1i2k3 unique_id: my_unique_id timestamp: 1646314054 unit_name: purchase_amount unit_value: 10 secret: very_secret ``` Join them as a URL query string, in the order shown. Every value must be URL-encoded the way it will appear in the final URL (`application/x-www-form-urlencoded`, as PHP's `http_build_query` does: a space becomes `+`, a `/` becomes `%2F`). The hash is calculated over the encoded string, so a value that is encoded differently in the URL no longer matches it. This generates the following initial parameter string: ```text version=v3&shop_uuid=abcde-1235-hijkl-1i2k3&unique_id=my_unique_id×tamp=1646314054&unit_name=purchase_amount&unit_value=10&secret=very_secret ``` ### Step 2: Hash the parameter string Using the [`SHA-1`](https://en.wikipedia.org/wiki/SHA-1) hash function, we can then convert this to a hash: ```text 60585d543ff8bbd07ad9798b44241981b4e2d423 ``` ### Step 3: Rebuild the parameter string We'll now rebuild the parameter string, much like the initial parameter string, with **two important changes**: - We will remove the `secret` parameter from the string - We will add the `hash` parameter at the end, using the hash created in step 2. This results in the following string (please make sure that the order of the parameters is exactly the same as displayed above): ```text version=v3&shop_uuid=abcde-1235-hijkl-1i2k3&unique_id=my_unique_id×tamp=1646314054&unit_name=purchase_amount&unit_value=10&hash=60585d543ff8bbd07ad9798b44241981b4e2d423 ``` ### Step 4: Create the final Loyalty Token URL The query string from step 3 can now be used to construct the final Loyalty Token URL. Each Client has their own URL to their Contacts Portal, which can be found in the [Contacts Portal settings](https://business.leat.com/apps/contacts-portal/overview/information). This can either be their own custom domain, e.g. https://acme.com, or a URL provided by the system, e.g. https://acme.app.leat.com. This will serve as the first part of the URL, followed by `/l?` and then the query string from step 3. ```text https://acme.com/l?version=v3&shop_uuid=abcde-1235-hijkl-1i2k3&unique_id=my_unique_id×tamp=1646314054&unit_name=purchase_amount&unit_value=10&hash=60585d543ff8bbd07ad9798b44241981b4e2d423 ``` The Loyalty Token is now ready to be sent! When a Contact visits the link and signs in to the Contacts Portal, the credits embedded in the Loyalty Token are added to their balance. Nothing is stored until the link is first opened. ## Create Loyalty Token Builds the Loyalty Token link for you, the same link the steps above produce, and returns its full URL. Send either `credits` or `unit_value` with its `unit_name`, not both. The timestamp is set by the server. Nothing is stored when the link is created: the Loyalty Token is saved the first time the link is opened, and the link can be claimed once. ```http POST https://api.leat.com/api/v3/oauth/clients/loyalty-tokens ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `version` *string, required* — The token version. Use v3: the link then carries the Shop's UUID. Any other value puts the Shop's id in the link instead. - `shop_id` *number, required if shop_uuid is missing* — The id of the Shop that is providing the credits. - `shop_uuid` *string, required if shop_id is missing* — The UUID of the Shop that is providing the credits. - `unique_id` *string, required* — A unique id from your system, e.g. the order ID. It must be unique within your loyalty program. - `credits` *number, required if unit_value is missing* — The number of credits (points) to issue, as an integer of at least 1. - `unit_value` *number, required if credits is missing* — The value points are calculated from with your earn rules, e.g. the order amount. - `unit_name` *string, required if unit_value is given* — The name of the Unit that unit_value is in, e.g. purchase_amount. **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `1008` | Shop not found. | | `60007` | Unit not found. | | `60022` | Please submit either credits or unit value. | **Response Example** ```json { "data": "https://krustykrab.sea/l?version=v3&shop_uuid=b7c1e4a2-5f3d-4e8a-9c21-7d6f0a3b2e19&unique_id=order-1042×tamp=1699811683&unit_name=purchase_amount&unit_value=12.5&hash=03e814f89efeea8c39d36825cb11ecfb96da4f93", "meta": [] } ``` ## Related - [Error handling](https://docs.leat.com/v3/error-handling.md) — Proper error handling is a crucial part of the integration - [Webhooks](https://docs.leat.com/v3/oauth/webhooks.md) — Learn about our new webhooks system Source: https://docs.leat.com/v3/oauth/loyalty-tokens --- # Loyalty Transaction Attributes Loyalty Transaction Attributes are Custom Attributes that you can link to your Loyalty Transactions, tailored to your specific needs. This functionality offers flexibility in collecting and organizing transaction-related data. ## List Loyalty Transaction Attributes Retrieve a list of all Loyalty Transaction Attributes associated with your Loyalty Program. It's a helpful tool to view both standard and Custom Attributes, allowing for better understanding and management of the data collected through loyalty transactions. ```http GET https://api.leat.com/api/v3/oauth/clients/loyalty-transaction-attributes ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "name": "plu", "label": "PLU", "placeholder": null, "type": "text", "field_type": "text", "options": [], "is_piggy_defined": false, "is_soft_read_only": false, "is_hard_read_only": false }, { "name": "category", "label": "Category", "placeholder": null, "type": "text", "field_type": "text", "options": [], "is_piggy_defined": false, "is_soft_read_only": false, "is_hard_read_only": false }, { "name": "server_name", "label": "Served by", "placeholder": null, "type": "text", "field_type": "text", "options": [], "is_piggy_defined": false, "is_soft_read_only": false, "is_hard_read_only": false }, ], "meta": {} } ``` ## Create Loyalty Transaction Attribute Creating a Loyalty Transaction Attribute allows you to add new Custom Attributes to your Loyalty Program. This feature is essential for tailoring the program to specific data collection needs, such as capturing unique customer preferences or transaction details. The creation process involves specifying the attribute's `name`, `data_type`, and other relevant details, enabling more personalized and effective data and loyalty management. ```http POST https://api.leat.com/api/v3/oauth/clients/loyalty-transaction-attributes ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — The unique value for the Loyalty Transaction Attribute, used for internal purposes. - `data_type` *string, required* — The data type of the attribute, possible options: url, text, date, phone, float, color, email, number, select, boolean, rich_text, date_time, long_text, date_range, time_range, identifier, birth_date, file_upload, media_upload, multi_select or license_plate. - `label` *string, optional* — The label for the attribute, which is outputted. - `description` *string, optional* — A description for the attribute. - `options` *array, optional* — Applicable for (multi)selects only. Array consisting of name and value arrays. **Response Example** ```json { "data": { "name": "favorite_color", "label": "Favorite Color", "placeholder": "", "type": "select", "field_type": "select", "options": { "blue": "Blue", "red": "Red", "yellow": "Yellow" }, "is_piggy_defined": false, "is_soft_read_only": false, "is_hard_read_only": false }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `60009` | Attribute already exists. | ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/loyalty-transaction-attributes --- # Loyalty Transactions Loyalty Transaction is the umbrella term for both Credit Receptions and Reward Receptions, representing all the changes to a Credit Balance. ## List Loyalty Transactions Returns transactions for a Loyalty Program. The results can be filtered by Contact, Shop and Loyalty Transaction `type`. ```http GET https://api.leat.com/api/v3/oauth/clients/loyalty-transactions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `limit` *number, optional* — Limit of records to retrieve. Default: 10; Maximum: 100. - `page` *number, optional* — Page to retrieve. Default: 1. - `type` *string, optional* — Type of Loyalty Transactions to be retrieved, options: 'credit_receptions' and 'reward_receptions'. If not set, loyalty transactions of all types are retrieved. - `shop_uuid` *string, optional* — Retrieve transactions performed at a specific Shop/Business Profile - `sort` *string, optional* — Sort by column. Ascending by default, prefix with a hyphen to sort descending (e.g. ?sort=-created_at) Possible values: - `created_at` — 'Sort by creation date. Prefix with '-' for descending. - `created_at` *array, optional* — Filter on creation date, can only be sent in as array. Date value must be in ISO8601 format. Combine to retrieve transactions between two dates. Possible values: - `created_at[gt]` — Filter on creation date is greater than value. - `created_at[gte]` — Filter on creation date is greater than or equal to value. - `created_at[lt]` — Filter on creation date is lower than value. - `created_at[lte]` — Filter on creation date is lower than or equal to value. **Response Example** ```json { "data": [ { "uuid": "daa78841-b321-4564-9dd3-91c6ccd9d600", "credits": 1177, "created_at": "2023-11-12T19:27:49+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": 1.15, "unit": { "id": 298, "name": "test2", "label": "Calories", "prefix": "t", "is_default": true } }, { "uuid": "9423bcfa-c2b1-41e6-998b-5c4182d1e259", "credits": 20, "created_at": "2023-11-12T17:37:41+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } }, { "uuid": "57589e9f-0107-4cdc-afb6-03b8c89d892e", "credits": 0, "created_at": "2023-11-10T16:47:40+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } }, { "uuid": "a45f1a18-bed8-4451-aba5-1a9e83fa5ea4", "credits": 0, "created_at": "2023-11-10T16:46:12+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } }, { "uuid": "a06e0546-48d4-4faf-91c6-f66989f5a49f", "credits": 0, "created_at": "2023-11-10T16:23:38+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } }, { "uuid": "e64ad300-668b-428d-850d-f4eb9b37268b", "credits": 10, "created_at": "2023-11-09T19:24:58+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } }, { "uuid": "8630aa53-0097-4beb-8504-4938f4ef724e", "credits": 10, "created_at": "2023-11-09T14:13:36+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } }, { "uuid": "ec2e9f1f-2573-4937-90f7-b610e881059b", "credits": 10, "created_at": "2023-11-09T14:12:44+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } }, { "uuid": "e075a8a4-6d50-4b30-a62b-142fb8a91a30", "credits": 10, "created_at": "2023-11-09T14:12:29+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123abc-def789-qwerty-34567", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123abc-def789-qwerty-34567", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } }, { "uuid": "13d3f57a-5566-42d0-8a63-328958f49144", "credits": 10, "created_at": "2023-11-09T14:11:32+00:00", "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 2929 } }, "shop": { "id": 15, "uuid": "123123", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "unit_value": null, "unit": { "id": 112, "name": "test", "label": "Calories", "prefix": "", "is_default": true } } ], "meta": { "page": 1, "limit": 10, "viewing_from": 1, "viewing_to": 10, "last_page": 6, "total": 57 } } ``` ## Related - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards - [Reward Receptions](https://docs.leat.com/v3/oauth/reward-receptions.md) — It's what Contacts are saving up for: Reward Receptions Source: https://docs.leat.com/v3/oauth/loyalty-transactions --- # Order Returns Order Returns represent reversals of previously created Orders in the Leat system. They are typically generated by external systems (e.g. web shops, POS systems, kiosks, etc.) when an order—or part of it—is refunded, or returned. In Leat, returns play a crucial role in accurately adjusting Loyalty, Marketing, Data and Automation flows. For example, a return may revoke previously awarded points or reverse a prepaid transaction. ## Create And Process Order Return Creates a new Order Return and processes it at once (i.e. handling corrections for the initial Order). It returns both the Order Return entity itself and a result object. If an Order Action was configured for the Account and the original Order triggered a Run, this is executed in sync and will populate the result object. An example of such a result would be a transaction containing loyalty points that reverses or corrects the points awarded during the original Order. ```http POST https://api.leat.com/api/v3/oauth/clients/order-returns/create-and-process ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `order` *object, required* — The corresponding original Order for this Order Return - `uuid` *string, required if external_identifier is missing* — UUID of the Order - `external_identifier` *string, required if uuid is missing* — External identifier of the Order - `external_identifier` *string, required* — A unique identifier from your system - `status` *string, required* — Status of the order return Possible values: - `PENDING` — Pending fulfilment - `COMPLETED` — Order Return has been completed. - `line_items` *array of objects, optional* — List of Line Items to be returned - `external_identifier` *string, required* — External identifier of the Line Item - `quantity` *number, required* — Quantity of the Line Item - `discount_amount` *number, optional* — Discount amount of the Line Item to be returned - `total_amount` *number, optional* — Total amount of the Line Item to be returned (after discounts). If not given, remaining quantity and price will be used to calculate the total amount to be returned, discounts may be ignored. - `reason` *string, optional* — The reason that the Line Item was returned - `sub_line_items` *array of objects, optional* — List of Sub Line Items that should be returned - `external_identifier` *string, required* — External identifier of the Sub Line Item - `quantity` *number, required* — Quantity of the Sub Line Item - `discount_amount` *number, optional* — Discount amount of the Sub Line Item to be returned - `total_amount` *number, optional* — Total amount of the Sub Line Item to be returned (after discounts). If not given, remaining quantity and price will be used to calculate the total amount to be returned, discounts may be ignored. - `reason` *string, optional* — The reason that the Sub Line Item was returned - `applied_discounts` *array of objects, optional* — List of Discounts applied to this Order - `external_identifier` *string, required* — External identifier of the Applied Discount - `amount` *number, required* — Amount to be returned - `charges` *array of objects, optional* — Any charges not included as line items. E.g. gratuity, service charges or shipping - `external_identifier` *string, required* — External identifier of the Charge - `amount` *number, optional* — The cost of the charge, before discounts, in the currency's lowest denomination, to be returned - `discount_amount` *number, optional* — The discount amount, in the currency's lowest denomination, to be returned - `total_amount` *number, required* — The total cost of the charge, after discounts, in the currency's lowest denomination, to be returned **Response Example** ```json { "data": { "return": { "uuid": "5558761f-5974-448e-ae6c-2efcf11f266c", "status": "COMPLETED", "order": { "uuid": "0f7c7bb3-5228-48c5-abdc-1b9cdcaacd9a" }, "line_item_returns": [ { "uuid": "7416cc03-3214-4b5b-8001-921bf4ff3163", "quantity": 5, "line_item": { "uuid": "19a237a3-4a00-400f-aeef-c7c976a87454" }, }, ], "sub_line_item_returns": [ { "uuid": "bd5cc183-715e-41f4-b305-34097ff04839", "quantity": 1, "sub_line_item": { "uuid": "b941af99-c19a-4066-8bd5-5952ac3207ea" }, }, ], }, "result": { "type": "POINTS_TRANSACTION", "data": { "points": 35, "new_balance": 2267, } } }, "meta": [] } ``` ## Related - [Orders](https://docs.leat.com/v3/oauth/orders.md) — Create and process orders - [Products](https://docs.leat.com/v3/oauth/products.md) — Manage products within your Leat system. Source: https://docs.leat.com/v3/oauth/order-returns --- # Orders Orders are replicas of the orders generated by external software (e.g. web shops, ordering apps, POS systems, kiosks et cetera), to be used by Leat as the driving force behind our Loyalty, Marketing, Data and Automation tools. In the Leat dashboard, clients can setup Actions to be triggered after the successful creation of an Order in the Leat system. This can result in Leat objects such as a Points Transaction or a Prepaid Transaction. Using Orders for Loyalty purposes requires two steps: 1. Creating the Order: Replicates the Order in the Leat system. 2. Processing the Order: Clients will set up their so-called Order Action in their dashboard to decide how to process the Order within the Leat system. This will usually entail converting the Order into a Points Transaction (currently the only option, to be extended in the near future). For some integrations, it makes sense to split the two steps, and only perform the second step under certain circumstances. For most integrations, however, both steps are required. To make it easier for those, a combined Create and Process Order endpoint has been added. ## New conventions **Please note:** this API call uses conventions that will be used in the V4 version of our API and from this points onwards. If you're already familiar with our API, take note of the following: - **'business\_profile' instead of 'shop':** This entity has already been renamed in the Leat dashboard for some time now and will be named as such in API calls. - **Refactor of Credit Receptions:** As part of a larger refactor of the core of our Loyalty engine, some new entities have been introduced, whilst others have been renamed. For this particular API, this means a `points_transaction` will be returned as a result instead of `credit_reception`. - **Input parameters:** For previous API endpoints, input parameters like a Contact's UUID were given as `contact_uuid`. In this newest endpoint, an object is expected instead. ## Create And Process Order RECOMMENDED Creates a new Order in the Leat system and directly processes it. Returns both the Order entity itself and a result object containing the result of the Order Action as configured by the Client. Currently, the only type of Order Action supported is the creation of a Points Transaction. However, please take into account that more result types will be added in the future (e.g. Prepaid Transaction). Note that all monetary amounts – like total\_order\_amount – are to be given in the currency's lowest denomination (e.g. €34,50 -> 3450). When creating Orders, the best practice is to create the Order as completely as you can. This includes linking Products and any Leat-actions that may have been undertaken in the Order process (e.g. Reward Redemptions, Voucher Redemptions, Giftcard Transactions etc.). Some best practices to take into account: - `external_identifier: ` This is the main identifier from your system, usually a UUID or GUID or some other. It's imperative this is unique across all Orders. - `order_amount, total_order_amount, total_discount_amount: ` These are mainly for visual and reference purposes. When Orders are using in Order Actions (to make Points Transactions, for instance), these values will be largely ignored. To account for product-specific exclusions and rules, the order amounts used in calculations and actions will be calculated using the line items, charges and discounts. Still, please ensure these match the content to prevent any confusion. - `line_items.external_identifier:` This is the unique identifier of this line item. Another line item of the same product should have their own unique identifier. - `line_items.total_amount:` The total amount of a line items ought to be the line items' price times the quantity, minus the discount amount. The total amount does not include the total amounts of any sub line items. They are counted separately. - `line_items.product:` For a host of features in Leat, it's imperative the product is linked (if present). The external identifier counts as the main unique identifier of the product, which is of course static for the same product across multiple line items and orders. If no product yet exists for the external identifier, one will be created. However, to fully enable the Product filtering feature, it's highly recommended to create the entire [product library](https://docs.leat.com/v3/oauth/products.md#create-product), including [categories](https://docs.leat.com/v3/oauth/categories.md#create-category), upon integration installation. - `line_items.sub_line_items:` Line items may include sub line items, like modifiers. **Note:** the total amounts of the sub line items count separately from the line item's total amount. Example: a line item for a 'Pizza Margherita' with a total amount of 1200 ($12) may include a sub line item for 'Extra Cheese' with a total amount of 200 ($2), which would bring the total order amount to 1400 ($14). The $2 from the sub line item is *not* included in the total amount on the parent line item. Sub line items are to be linked to products in the same way as their parent line items. - `line_items.result:` This is the 'Leat result' of this line item, if any exists. Examples of this is when a customer buys a Gift card or a Voucher. This would be reflected as a line item on the Order and the resulting Giftcard Transaction (usually created after payment has succeeded) can be linked as a result. Highly recommended to link if present. - `applied_discounts:` If one or more discounts have been applied to the order, these should be added here. Discounts are either applied on an Order level, or on a (Sub)Line Item level. In the latter, the applied discount should also be reflected in the discount amount on the individual (sub) line item(s) and/or charges. In such a case, the specific items should also be linked to the discount via the line items, sub line items and charges parameters. - `applied_discounts.offer:` If the applied discount was the result of a Leat offer (e.g. Reward Redemption, Voucher Redemption etc.), this offer should be added to the discount. Highly recommended to link if present. - `charges:` Charges are costs that are applied after (sub) line items. Examples include gratuity, service charges, shipping, wrapping or taxes (all of these are default Charge Definitions). In the case of taxes, there are only a few use cases in which taxes are to be included as separate charges. Most often, it's preferable to include them in the line items price instead of as separate charges. Charge Definitions are what Products are to Line Items. - `payments:` Currently, only Leat-specific payments can be added here. Payment methods like credit card or cash are not to be sent in. If (part of) the order was paid using Leat payment methods (Giftcards, Prepaid), these should be linked here. If you have any questions about Orders, please reach out to your partnership manager or contact us at dev@leat.com. ```http POST https://api.leat.com/api/v3/oauth/clients/orders/create-and-process ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `reference` *string, optional* — A human readable identifier from your system, for reference purposes only - `status` *string, required* — Status of the order Possible values: - `CREATED` — Order has just been created, not yet completed - `PENDING` — Pending fulfilment and/or payment - `COMPLETED` — Order has been completed - `CANCELLED` — Order was cancelled before completion - `payment_status` *string, required* — Status of the payment of the order Possible values: - `UNPAID` — Payment process has not been started - `PENDING` — Payment process started, pending confirmation - `PAID` — Payment successful - `total_order_amount` *number, required* — Total order amount, including all discounts and charges, in the currency's lowest denomination. - `order_amount` *number, optional* — Sum of total amounts of all (sub) line items – including item-based discounts – before order-based discounts and charges, in the currency's lowest denomination. - `total_discount_amount` *number, optional* — Total discount amount, in the currency's lowest denomination - `currency` *string, optional* — Order currency in ISO 4217 format; defaults to Account's default currency - `paid_at` *string, optional* — The date and time this Order was paid, in ISO 8601 format - `completed_at` *string, optional* — The date and time this Order was completed, in ISO 8601 format - `contact` *object, required* — Contact who made the Order - `uuid` *string, required* — UUID of the Contact - `business_profile` *object, required* — Location where the Order took place - `uuid` *string, required* — UUID of the Business Profile - `line_items` *array of objects, required* — List of Products in this Order - `external_identifier` *string, required* — A unique identifier for this Line Item - `name` *string, required* — Name of the Line Item - `quantity` *number, required* — Quantity of the Line Item - `price` *number, required* — Price per unit of this Line Item - `total_amount` *number, required* — Total amount of this Line Item, in the currency's lowest denomination, after discounts - `discount_amount` *number, optional* — Discount amount of this Line Item, in the currency's lowest denomination - `product` *object, optional* — Associated Product, which enables the Product filtering feature, optional but highly recommended. - `external_identifier` *string, required* — Unique identifier to identify the Product, Product will be created using external_identifier and line_item.name if not found - `result` *object, optional* — Resulting Leat object, if applicable. Possible options: Giftcard Transaction, Prepaid Transaction and Voucher. - `uuid` *string, required* — UUID used to identify the resulting Leat object - `type` *string, required* — Type of the resulting Leat object Possible values: - `GIFTCARD_TRANSACTION` — Giftcard Transaction - `PREPAID_TRANSACTION` — Prepaid Transaction - `VOUCHER` — Voucher - `sub_line_items` *array of objects, optional* — List of Sub Line Items of this Line Item - `external_identifier` *string, required* — A unique identifier for this Sub Line Item - `name` *string, required* — Name of the Sub Line Item - `quantity` *number, required* — Quantity of the Sub Line Item - `price` *number, required* — Price per unit of this Sub Line Item - `total_amount` *number, required* — Total amount of this Sub Line Item, in the currency's lowest denomination, after discounts - `discount_amount` *number, optional* — Discount amount of this Sub Line Item, in the currency's lowest denomination - `product` *object, optional* — Associated Product, which enables the Product filtering feature, optional but highly recommended. - `external_identifier` *string, required* — Unique identifier to identify the Product, Product will be created using external_identifier and sub_line_item.name if not found - `applied_discounts` *array of objects, optional* — List of Discounts applied to this Order - `external_identifier` *string, required* — Unique identifier to the Discount - `name` *string, required* — Name of the Discount - `amount` *number, required* — Resulting monetary discount amount in the currency's lowest denomination - `type` *string, optional* — Type of the Discount Possible values: - `PERCENTAGE` — For percentage based discounts - `ABSOLUTE` — For absolute discounts - `value` *number, optional* — The value of either the percentage or absolute discount - `applied_to` *string, required* — Indicates if Discount is applied to Order or to one or more (Sub) Line Items Possible values: - `ORDER` — Indicates Discount is applied on an Order level - `ORDER_LINES` — Indicates Discount is applied to individual Line Items, Sub Line Items, and/or Charges - `line_items` *array of objects, optional* — List of Line Items to which this Discount was applied - `external_identifier` *string, required* — Unique identifier to the Line Item with which it's identified under line_items - `sub_line_items` *array of objects, optional* — List of Sub Line Items to which this Discount was applied - `external_identifier` *string, required* — Unique identifier to the Sub Line Item with which it's identified under sub_line_items - `charges` *array of objects, optional* — List of Charges to which this Discount was applied - `external_identifier` *string, required* — Unique identifier to the Charge with which it's identified under charges - `offer` *object, optional* — Corresponding Leat Offer if applicable. Possible options: Reward Redemption, Collectable Reward Redemption and Voucher Redemption. - `uuid` *string, required* — UUID used to identify the Leat offer redemption - `type` *string, required* — Type of the Leat Offer Possible values: - `REWARD_REDEMPTION` — Reward Redemption - `COLLECTABLE_REWARD_REDEMPTION` — Collectable Reward Redemption - `VOUCHER_REDEMPTION` — Voucher Redemption - `charges` *array of objects, optional* — Any charges not included as line items. E.g. gratuity, service charges or shipping - `external_identifier` *string, required* — Unique identifier to the Charge - `type` *string, optional* *(deprecated)* — Deprecated: Please use charge_definition instead. - `name` *string, required* — A human readable reference to the charge - `amount` *number, optional* — The cost of the charge, before discounts, in the currency's lowest denomination - `discount_amount` *number, optional* — The discount amount, in the currency's lowest denomination - `total_amount` *number, required* — The total cost of the charge, after discounts, in the currency's lowest denomination - `charge_definition` *object, optional* — The charge definition, for use in rules and exclusions. Every account has a few definitions by default, with external identifiers: SERVICE_CHARGE. Possible values: - `SERVICE_CHARGE` — A service charge applied to the order - `GRATUITY` — Gratuity or tip - `SHIPPING` — Shipping costs - `WRAPPING` — Wrapping costs - `TAX` — Separate tax charges. Do not add if taxes are already included in line item total amounts. - `external_identifier` *string, required* — Unique identifier to identify the Charge Definition, Charge Definition will be created using external_identifier and charge.name if not found - `payments` *array of objects, optional* — Any payments made for this Order using Leat objects. Note: currently accepts Giftcard Transactions and Prepaid Transactions only. - `uuid` *string, required* — UUID used to identify the Leat Payment - `type` *string, required* — Type of Leat Payment Possible values: - `GIFTCARD_TRANSACTION` — Giftcard Transaction - `PREPAID_TRANSACTION` — Prepaid Transaction **Response Example** ```json { "data": { "order": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "12345-abcdefg-6789-yuiop", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "12345-abcdefg-6789-yuiop", "name": "HeadQuarters" }, "external_identifier": "EXT-0012", "reference": "Order B-2110", "status": "COMPLETED", "payment_status": "PAID", "currency": "EUR", "formatted_total_order_amount": "€ 33.31", "order_amount": 3175, "total_charges_amount": 381, "total_discount_amount": 225, "total_order_amount": 3331, "paid_at": "2025-05-01T20:10:44+00:00", "completed_at": "2025-05-01T20:11:03+00:00", "line_items": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "LI_IT_11090_lkajsf", "name": "Double Krabby Patty", "quantity": 2, "price": 1250, "total_amount": 2500, "discount_amount": 0, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, "sub_line_items": [ { "uuid": "87a4be97-73dd-40a4", "external_identifier": "SU_LI_IT_11090_lkajsf", "name": "Extra Secret Sauce", "quantity": 1, "price": 500, "total_amount": 500, "discount_amount": 0, "product": { "external_identifier": "PR-15-454", "name": "Extra Secret Sauce", } } ] }, { "uuid": "187-8Dfadf3-lsdf234ds-99ass", "external_identifier": "LI_IT_991_Ac990s", "name": "Kelp Soda", "quantity": 2, "price": 450, "total_amount": 675, "discount_amount": 225, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, }, ], "applied_discounts": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "DISC_25_OFF_DRINKS", "name": "25% off Drinks", "amount": 225, "type": "PERCENTAGE", "value": 25, "applied_to": "LINE_ITEM", "line_items": [ { "external_identifier": "LI_IT_991_Ac990s", }, ], }, "charges": [ { "external_identifier": "SRV_CH-12", "type": "SERVICE_CHARGE", "name": "Service charge: 12%", "amount": 381, "discount_amount": 0, "total_amount": 0 }, ], "created_at": "2025-05-01T20:11:58+00:00", "updated_at": "2025-05-01T20:11:58+00:00" }, "result": { "type": "POINTS_TRANSACTION", "data": { "points": 35, "new_balance": 2267, } } }, "meta": [] } ``` ## Create Order Creates a new Order in the Leat system. It does not yet process the Order, so no Points Transaction will be created just yet. Call the Process Order endpoint to convert the Order into a Points Transaction. Alternatively, use the Create and Process Order endpoint instead. Note that all monetary amounts – like total\_order\_amount – are to be given in the currency's lowest denomination (e.g. €34,50 -> 3450). When creating Orders, the best practice is to create the Order as completely as you can. This includes linking Products and any Leat-actions that may have been undertaken in the Order process (e.g. Reward Redemptions, Voucher Redemptions, Giftcard Transactions etc.). Some best practices to take into account: - `external_identifier: ` This is the main identifier from your system, usually a UUID or GUID or some other. It's imperative this is unique across all Orders. - `order_amount, total_order_amount, total_discount_amount: ` These are mainly for visual and reference purposes. When Orders are using in Order Actions (to make Points Transactions, for instance), these values will be largely ignored. To account for product-specific exclusions and rules, the order amounts used in calculations and actions will be calculated using the line items, charges and discounts. Still, please ensure these match the content to prevent any confusion. - `line_items.external_identifier:` This is the unique identifier of this line item. Another line item of the same product should have their own unique identifier. - `line_items.total_amount:` The total amount of a line items ought to be the line items' price times the quantity, minus the discount amount. The total amount does not include the total amounts of any sub line items. They are counted separately. - `line_items.product:` For a host of features in Leat, it's imperative the product is linked (if present). The external identifier counts as the main unique identifier of the product, which is of course static for the same product across multiple line items and orders. If no product yet exists for the external identifier, one will be created. However, to fully enable the Product filtering feature, it's highly recommended to create the entire [product library](https://docs.leat.com/v3/oauth/products.md#create-product), including [categories](https://docs.leat.com/v3/oauth/categories.md#create-category), upon integration installation. - `line_items.sub_line_items:` Line items may include sub line items, like modifiers. **Note:** the total amounts of the sub line items count separately from the line item's total amount. Example: a line item for a 'Pizza Margherita' with a total amount of 1200 ($12) may include a sub line item for 'Extra Cheese' with a total amount of 200 ($2), which would bring the total order amount to 1400 ($14). The $2 from the sub line item is *not* included in the total amount on the parent line item. Sub line items are to be linked to products in the same way as their parent line items. - `line_items.result:` This is the 'Leat result' of this line item, if any exists. Examples of this is when a customer buys a Gift card or a Voucher. This would be reflected as a line item on the Order and the resulting Giftcard Transaction (usually created after payment has succeeded) can be linked as a result. Highly recommended to link if present. - `applied_discounts:` If one or more discounts have been applied to the order, these should be added here. Discounts are either applied on an Order level, or on a (Sub)Line Item level. In the latter, the applied discount should also be reflected in the discount amount on the individual (sub) line item(s) and/or charges. In such a case, the specific items should also be linked to the discount via the line items, sub line items and charges parameters. - `applied_discounts.offer:` If the applied discount was the result of a Leat offer (e.g. Reward Redemption, Voucher Redemption etc.), this offer should be added to the discount. Highly recommended to link if present. - `charges:` Charges are costs that are applied after (sub) line items. Examples include gratuity, service charges, shipping, wrapping or taxes (all of these are default Charge Definitions). In the case of taxes, there are only a few use cases in which taxes are to be included as separate charges. Most often, it's preferable to include them in the line items price instead of as separate charges. Charge Definitions are what Products are to Line Items. - `payments:` Currently, only Leat-specific payments can be added here. Payment methods like credit card or cash are not to be sent in. If (part of) the order was paid using Leat payment methods (Giftcards, Prepaid), these should be linked here. If you have any questions about Orders, please reach out to your partnership manager or contact us at dev@leat.com. ```http POST https://api.leat.com/api/v3/oauth/clients/orders ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `reference` *string, optional* — A human readable identifier from your system, for reference purposes only - `status` *string, required* — Status of the order Possible values: - `CREATED` — Order has just been created, not yet completed - `PENDING` — Pending fulfilment and/or payment - `COMPLETED` — Order has been completed - `CANCELLED` — Order was cancelled before completion - `payment_status` *string, required* — Status of the payment of the order Possible values: - `UNPAID` — Payment process has not been started - `PENDING` — Payment process started, pending confirmation - `PAID` — Payment successful - `total_order_amount` *number, required* — Total order amount, including all discounts and charges, in the currency's lowest denomination. - `order_amount` *number, optional* — Sum of total amounts of all (sub) line items – including item-based discounts – before order-based discounts and charges, in the currency's lowest denomination. - `total_discount_amount` *number, optional* — Total discount amount, in the currency's lowest denomination - `currency` *string, optional* — Order currency in ISO 4217 format; defaults to Account's default currency - `paid_at` *string, optional* — The date and time this Order was paid, in ISO 8601 format - `completed_at` *string, optional* — The date and time this Order was completed, in ISO 8601 format - `contact` *object, required* — Contact who made the Order - `uuid` *string, required* — UUID of the Contact - `business_profile` *object, required* — Location where the Order took place - `uuid` *string, required* — UUID of the Business Profile - `line_items` *array of objects, required* — List of Products in this Order - `external_identifier` *string, required* — A unique identifier for this Line Item - `name` *string, required* — Name of the Line Item - `quantity` *number, required* — Quantity of the Line Item - `price` *number, required* — Price per unit of this Line Item - `total_amount` *number, required* — Total amount of this Line Item, in the currency's lowest denomination, after discounts - `discount_amount` *number, optional* — Discount amount of this Line Item, in the currency's lowest denomination - `product` *object, optional* — Associated Product, which enables the Product filtering feature, optional but highly recommended. - `external_identifier` *string, required* — Unique identifier to identify the Product, Product will be created using external_identifier and line_item.name if not found - `result` *object, optional* — Resulting Leat object, if applicable. Possible options: Giftcard Transaction, Prepaid Transaction and Voucher. - `uuid` *string, required* — UUID used to identify the resulting Leat object - `type` *string, required* — Type of the resulting Leat object Possible values: - `GIFTCARD_TRANSACTION` — Giftcard Transaction - `PREPAID_TRANSACTION` — Prepaid Transaction - `VOUCHER` — Voucher - `sub_line_items` *array of objects, optional* — List of Sub Line Items of this Line Item - `external_identifier` *string, required* — A unique identifier for this Sub Line Item - `name` *string, required* — Name of the Sub Line Item - `quantity` *number, required* — Quantity of the Sub Line Item - `price` *number, required* — Price per unit of this Sub Line Item - `total_amount` *number, required* — Total amount of this Sub Line Item, in the currency's lowest denomination, after discounts - `discount_amount` *number, optional* — Discount amount of this Sub Line Item, in the currency's lowest denomination - `product` *object, optional* — Associated Product, which enables the Product filtering feature, optional but highly recommended. - `external_identifier` *string, required* — Unique identifier to identify the Product, Product will be created using external_identifier and sub_line_item.name if not found - `applied_discounts` *array of objects, optional* — List of Discounts applied to this Order - `external_identifier` *string, required* — Unique identifier to the Discount - `name` *string, required* — Name of the Discount - `amount` *number, required* — Resulting monetary discount amount in the currency's lowest denomination - `type` *string, optional* — Type of the Discount Possible values: - `PERCENTAGE` — For percentage based discounts - `ABSOLUTE` — For absolute discounts - `value` *number, optional* — The value of either the percentage or absolute discount - `applied_to` *string, required* — Indicates if Discount is applied to Order or to one or more (Sub) Line Items Possible values: - `ORDER` — Indicates Discount is applied on an Order level - `ORDER_LINES` — Indicates Discount is applied to individual Line Items, Sub Line Items, and/or Charges - `line_items` *array of objects, optional* — List of Line Items to which this Discount was applied - `external_identifier` *string, required* — Unique identifier to the Line Item with which it's identified under line_items - `sub_line_items` *array of objects, optional* — List of Sub Line Items to which this Discount was applied - `external_identifier` *string, required* — Unique identifier to the Sub Line Item with which it's identified under sub_line_items - `charges` *array of objects, optional* — List of Charges to which this Discount was applied - `external_identifier` *string, required* — Unique identifier to the Charge with which it's identified under charges - `offer` *object, optional* — Corresponding Leat Offer if applicable. Possible options: Reward Redemption, Collectable Reward Redemption and Voucher Redemption. - `uuid` *string, required* — UUID used to identify the Leat offer redemption - `type` *string, required* — Type of the Leat Offer Possible values: - `REWARD_REDEMPTION` — Reward Redemption - `COLLECTABLE_REWARD_REDEMPTION` — Collectable Reward Redemption - `VOUCHER_REDEMPTION` — Voucher Redemption - `charges` *array of objects, optional* — Any charges not included as line items. E.g. gratuity, service charges or shipping - `external_identifier` *string, required* — Unique identifier to the Charge - `type` *string, optional* *(deprecated)* — Deprecated: Please use charge_definition instead. - `name` *string, required* — A human readable reference to the charge - `amount` *number, optional* — The cost of the charge, before discounts, in the currency's lowest denomination - `discount_amount` *number, optional* — The discount amount, in the currency's lowest denomination - `total_amount` *number, required* — The total cost of the charge, after discounts, in the currency's lowest denomination - `charge_definition` *object, optional* — The charge definition, for use in rules and exclusions. Every account has a few definitions by default, with external identifiers: SERVICE_CHARGE. Possible values: - `SERVICE_CHARGE` — A service charge applied to the order - `GRATUITY` — Gratuity or tip - `SHIPPING` — Shipping costs - `WRAPPING` — Wrapping costs - `TAX` — Separate tax charges. Do not add if taxes are already included in line item total amounts. - `external_identifier` *string, required* — Unique identifier to identify the Charge Definition, Charge Definition will be created using external_identifier and charge.name if not found - `payments` *array of objects, optional* — Any payments made for this Order using Leat objects. Note: currently accepts Giftcard Transactions and Prepaid Transactions only. - `uuid` *string, required* — UUID used to identify the Leat Payment - `type` *string, required* — Type of Leat Payment Possible values: - `GIFTCARD_TRANSACTION` — Giftcard Transaction - `PREPAID_TRANSACTION` — Prepaid Transaction **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "12345-abcdefg-6789-yuiop", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "12345-abcdefg-6789-yuiop", "name": "HeadQuarters" }, "external_identifier": "EXT-0012", "reference": "Order B-2110", "status": "COMPLETED", "payment_status": "PAID", "currency": "EUR", "formatted_total_order_amount": "€ 33.31", "order_amount": 3175, "total_charges_amount": 381, "total_discount_amount": 225, "total_order_amount": 3331, "paid_at": "2025-05-01T20:10:44+00:00", "completed_at": "2025-05-01T20:11:03+00:00", "line_items": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "LI_IT_11090_lkajsf", "name": "Double Krabby Patty", "quantity": 2, "price": 1250, "total_amount": 2500, "discount_amount": 0, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, "sub_line_items": [ { "uuid": "87a4be97-73dd-40a4", "external_identifier": "SU_LI_IT_11090_lkajsf", "name": "Extra Secret Sauce", "quantity": 1, "price": 500, "total_amount": 500, "discount_amount": 0, "product": { "external_identifier": "PR-15-454", "name": "Extra Secret Sauce", } } ] }, { "uuid": "187-8Dfadf3-lsdf234ds-99ass", "external_identifier": "LI_IT_991_Ac990s", "name": "Kelp Soda", "quantity": 2, "price": 450, "total_amount": 675, "discount_amount": 225, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, }, ], "applied_discounts": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "DISC_25_OFF_DRINKS", "name": "25% off Drinks", "amount": 225, "type": "PERCENTAGE", "value": 25, "applied_to": "LINE_ITEM", "line_items": [ { "external_identifier": "LI_IT_991_Ac990s", }, ], }, "charges": [ { "external_identifier": "SRV_CH-12", "type": "SERVICE_CHARGE", "name": "Service charge: 12%", "amount": 381, "discount_amount": 0, "total_amount": 0 }, ], "created_at": "2025-05-01T20:11:58+00:00", "updated_at": "2025-05-01T20:11:58+00:00" }, "meta": [] } ``` ## Process Order Processes an existing Order in the Leat system and returns a result object. If an Order Action was configured for the Account, then this is executed in sync and will fill the result object. An example of such a result would be a transaction containing loyalty points. ```http POST https://api.leat.com/api/v3/oauth/clients/orders/{uuid}/process ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `uuid` *string, required* — The uuid of the order in your Leat system **Response Example** ```json { "data": { "type": "POINTS_TRANSACTION", "data": { "points": 35, "new_balance": 2267, } }, "meta": [] } ``` ## Find Order Use the `external_identifier` to find an Order. ```http GET https://api.leat.com/api/v3/oauth/clients/orders/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "12345-abcdefg-6789-yuiop", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "12345-abcdefg-6789-yuiop", "name": "HeadQuarters" }, "external_identifier": "EXT-0012", "reference": "Order B-2110", "status": "COMPLETED", "payment_status": "PAID", "currency": "EUR", "formatted_total_order_amount": "€ 33.31", "order_amount": 3175, "total_charges_amount": 381, "total_discount_amount": 225, "total_order_amount": 3331, "paid_at": "2025-05-01T20:10:44+00:00", "completed_at": "2025-05-01T20:11:03+00:00", "line_items": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "LI_IT_11090_lkajsf", "name": "Double Krabby Patty", "quantity": 2, "price": 1250, "total_amount": 2500, "discount_amount": 0, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, "sub_line_items": [ { "uuid": "87a4be97-73dd-40a4", "external_identifier": "SU_LI_IT_11090_lkajsf", "name": "Extra Secret Sauce", "quantity": 1, "price": 500, "total_amount": 500, "discount_amount": 0, "product": { "external_identifier": "PR-15-454", "name": "Extra Secret Sauce", } } ] }, { "uuid": "187-8Dfadf3-lsdf234ds-99ass", "external_identifier": "LI_IT_991_Ac990s", "name": "Kelp Soda", "quantity": 2, "price": 450, "total_amount": 675, "discount_amount": 225, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, }, ], "applied_discounts": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "DISC_25_OFF_DRINKS", "name": "25% off Drinks", "amount": 225, "type": "PERCENTAGE", "value": 25, "applied_to": "LINE_ITEM", "line_items": [ { "external_identifier": "LI_IT_991_Ac990s", }, ], }, "charges": [ { "external_identifier": "SRV_CH-12", "type": "SERVICE_CHARGE", "name": "Service charge: 12%", "amount": 381, "discount_amount": 0, "total_amount": 0 }, ], "created_at": "2025-05-01T20:11:58+00:00", "updated_at": "2025-05-01T20:11:58+00:00" }, "meta": [] } ``` ## List Orders Retrieve a paginated list of Orders, ordered by most recent. ```http GET https://api.leat.com/api/v3/oauth/clients/orders ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `limit` *number, optional* — Number of Orders to retrieve (max: 30, default: 30) - `page` *number, optional* — Page number to retrieve (default: 1) - `contact_uuid` *string, optional* — Retrieve Orders for a specific Contact - `business_profile_uuid` *string, optional* — Retrieve Orders for a specific Business Profile **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "12345-abcdefg-6789-yuiop", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "12345-abcdefg-6789-yuiop", "name": "HeadQuarters" }, "external_identifier": "EXT-0012", "reference": "Order B-2110", "status": "COMPLETED", "payment_status": "PAID", "currency": "EUR", "formatted_total_order_amount": "€ 33.31", "order_amount": 3175, "total_charges_amount": 381, "total_discount_amount": 225, "total_order_amount": 3331, "paid_at": "2025-05-01T20:10:44+00:00", "completed_at": "2025-05-01T20:11:03+00:00", "line_items": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "LI_IT_11090_lkajsf", "name": "Double Krabby Patty", "quantity": 2, "price": 1250, "total_amount": 2500, "discount_amount": 0, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, "sub_line_items": [ { "uuid": "87a4be97-73dd-40a4", "external_identifier": "SU_LI_IT_11090_lkajsf", "name": "Extra Secret Sauce", "quantity": 1, "price": 500, "total_amount": 500, "discount_amount": 0, "product": { "external_identifier": "PR-15-454", "name": "Extra Secret Sauce", } } ] }, { "uuid": "187-8Dfadf3-lsdf234ds-99ass", "external_identifier": "LI_IT_991_Ac990s", "name": "Kelp Soda", "quantity": 2, "price": 450, "total_amount": 675, "discount_amount": 225, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, }, ], "applied_discounts": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "DISC_25_OFF_DRINKS", "name": "25% off Drinks", "amount": 225, "type": "PERCENTAGE", "value": 25, "applied_to": "LINE_ITEM", "line_items": [ { "external_identifier": "LI_IT_991_Ac990s", }, ], }, "charges": [ { "external_identifier": "SRV_CH-12", "type": "SERVICE_CHARGE", "name": "Service charge: 12%", "amount": 381, "discount_amount": 0, "total_amount": 0 }, ], "created_at": "2025-05-01T20:11:58+00:00", "updated_at": "2025-05-01T20:11:58+00:00" }, "meta": [] } ``` ## Update Order Use the `uuid` to update an existing Order. ```http PUT https://api.leat.com/api/v3/oauth/clients/orders/{uuid} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `status` *string, optional* — Status of the order Possible values: - `CREATED` — Order has just been created, not yet completed - `PENDING` — Pending fulfilment and/or payment - `COMPLETED` — Order has been completed - `CANCELLED` — Order was cancelled before completion - `payment_status` *string, optional* — Status of the payment of the order Possible values: - `UNPAID` — Payment process has not been started - `PENDING` — Payment process started, pending confirmation - `PAID` — Payment successful **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "12345-abcdefg-6789-yuiop", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "12345-abcdefg-6789-yuiop", "name": "HeadQuarters" }, "external_identifier": "EXT-0012", "reference": "Order B-2110", "status": "COMPLETED", "payment_status": "PAID", "currency": "EUR", "formatted_total_order_amount": "€ 33.31", "order_amount": 3175, "total_charges_amount": 381, "total_discount_amount": 225, "total_order_amount": 3331, "paid_at": "2025-05-01T20:10:44+00:00", "completed_at": "2025-05-01T20:11:03+00:00", "line_items": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "LI_IT_11090_lkajsf", "name": "Double Krabby Patty", "quantity": 2, "price": 1250, "total_amount": 2500, "discount_amount": 0, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, "sub_line_items": [ { "uuid": "87a4be97-73dd-40a4", "external_identifier": "SU_LI_IT_11090_lkajsf", "name": "Extra Secret Sauce", "quantity": 1, "price": 500, "total_amount": 500, "discount_amount": 0, "product": { "external_identifier": "PR-15-454", "name": "Extra Secret Sauce", } } ] }, { "uuid": "187-8Dfadf3-lsdf234ds-99ass", "external_identifier": "LI_IT_991_Ac990s", "name": "Kelp Soda", "quantity": 2, "price": 450, "total_amount": 675, "discount_amount": 225, "product": { "external_identifier": "PR-12-401", "name": "Double Krabby Patty", }, }, ], "applied_discounts": [ { "uuid": "021as-8892k-ljkafds-98as", "external_identifier": "DISC_25_OFF_DRINKS", "name": "25% off Drinks", "amount": 225, "type": "PERCENTAGE", "value": 25, "applied_to": "LINE_ITEM", "line_items": [ { "external_identifier": "LI_IT_991_Ac990s", }, ], }, "charges": [ { "external_identifier": "SRV_CH-12", "type": "SERVICE_CHARGE", "name": "Service charge: 12%", "amount": 381, "discount_amount": 0, "total_amount": 0 }, ], "created_at": "2025-05-01T20:11:58+00:00", "updated_at": "2025-05-01T20:11:58+00:00" }, "meta": [] } ``` ## Simulate Order Result Simulates the result of a new Order in the Leat system. Please note that the actual result may differ, as some time-based rules may be in place. Also, some rules may not be applied if the required information is missing from the payload (e.g. contact). ```http POST https://api.leat.com/api/v3/oauth/clients/orders/simulate ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `status` *string, optional* — Status of the order Possible values: - `CREATED` — Order has just been created, not yet completed - `PENDING` — Pending fulfilment - `COMPLETED` — When created - `payment_status` *string, optional* — Status of the payment of the order Possible values: - `UNPAID` — Payment process has not been started - `PENDING` — Payment process started, pending confirmation - `PAID` — Payment successful - `total_order_amount` *number, required* — Total order amount, after discounts and other charges, in the currency's lowest denomination - `order_amount` *number, optional* — Total order amount, before discounts and other charges, in the currency's lowest denomination - `total_discount_amount` *number, optional* — Total discount amount, in the currency's lowest denomination - `currency` *string, optional* — Order currency in ISO 4217 format; defaults to Account's default currency - `contact` *object, optional* — The Contact for this Order. If not given, Contact-based rules cannot be applied - `uuid` *string, required* — UUID of the Contact - `business_profile` *object, required* — Location where the Order takes place - `uuid` *string, required* — UUID of the Business Profile - `line_items` *array of objects, required* — List of Products in this Order - `name` *string, required* — Name of the Line Item - `quantity` *number, required* — Quantity of the Line Item - `price` *number, required* — Price per unit of this Line Item - `total_amount` *number, required* — Total amount of this Line Item, in the currency's lowest denomination - `discount_amount` *number, optional* — Discount amount of this Line Item, in the currency's lowest denomination - `product` *object, optional* — Associated Product - `uuid` *string, optional* — UUID to the Product - `external_identifier` *string, optional* — Unique identifier to the Product with which it's registered in Leat - `sub_line_items` *array of objects, optional* — List of Sub Line Items of this Line Item - `name` *string, required* — Name of the Sub Line Item - `quantity` *number, required* — Quantity of the Sub Line Item - `price` *number, required* — Price per unit of this Sub Line Item - `total_amount` *number, required* — Total amount of this Sub Line Item, in the currency's lowest denomination - `discount_amount` *number, optional* — Discount amount of this Sub Line Item, in the currency's lowest denomination - `product` *object, optional* — Associated Product - `uuid` *string, optional* — UUID to the Product - `external_identifier` *string, optional* — Unique identifier to the Product with which it's registered in Leat - `applied_discounts` *array of objects, optional* — List of Discounts applied to this Order - `name` *string, required* — Name of the Discount - `amount` *number, required* — Resulting monetary discount amount in the currency's lowest denomination - `type` *string, optional* — Type of the Discount Possible values: - `PERCENTAGE` — For percentage based discounts - `ABSOLUTE` — For absolute discounts - `value` *number, optional* — The value of either the percentage or absolute discount - `applied_to` *string, required* — Indicates if Discount is applied to Order or to one or more (Sub) Line Items. Discounts applied on (Sub) Line Items can be excluded for the Order Simulation. Possible values: - `ORDER` — For Order level discounts - `LINE_ITEMS` — For Line Item level discounts - `SUB_LINE_ITEMS` — For Sub Line Item level discounts - `charges` *array of objects, optional* — Any charges not included as line items. E.g. gratuity, service charges or shipping - `name` *string, required* — A human readable reference to the charge - `amount` *number, optional* — The cost of the charge, before discounts, in the currency's lowest denomination - `discount_amount` *number, optional* — The discount amount, in the currency's lowest denomination - `total_amount` *number, required* — The total cost of the charge, after discounts, in the currency's lowest denomination - `charge_definition` *object, optional* — The charge definition, for use in rules and exclusions. Every account has a few definitions by default, with external identifiers: SERVICE_CHARGE. Possible values: - `SERVICE_CHARGE` — A service charge applied to the order - `GRATUITY` — Gratuity or tip - `SHIPPING` — Shipping costs - `WRAPPING` — Wrapping costs - `TAX` — Separate tax charges. Do not add if taxes are already included in line item total amounts. - `uuid` *string, required if* — UUID to identify the Charge Definition - `external_identifier` *string, required if* — Unique identifier to identify the Charge Definition (use for default Charge Definitions) **Response Example** ```json { "data": { "type": "points_transaction", "data" : { "points": 35 }, }, "meta": [] } ``` ## Related - [Order Returns](https://docs.leat.com/v3/oauth/order-returns.md) — Create and process order returns - [Products](https://docs.leat.com/v3/oauth/products.md) — Manage products within your Leat system. Source: https://docs.leat.com/v3/oauth/orders --- # Perks Perks are benefits associated with different Tiers in our system, designed to enhance customer engagement and loyalty. Each tier, such as Bronze, Silver, and Gold, comes with specific perks like percentage discounts or free delivery. These Perks can be retrieved via our API to implement and manage the associated benefits, like applying set discount Perks. ## List Perks This API call fetches a list of all Perks with their Perk Options. ```http GET https://api.leat.com/api/v3/oauth/clients/perks ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "label": "Discount", "name": "discount", "data_type": "single_select", "options": [ { "label": "No discount", "value": "0_off", "default": true, }, { "label": "10% off", "value": "10_off", "default": false, }, { "label": "25% off", "value": "25_off", "default": false, } ] } ] } ``` ## Create Perk Creates a new Perk. ```http POST https://api.leat.com/api/v3/oauth/clients/perks ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `label` *string, required* — The label of the perk. - `name` *string, required* — The name of the perk (used internally). - `data_type` *string, required* — The data type of the perk, available options: single_select or boolean. - `options` *array, required* — The options of the perk (should contain a label, value and default key with values). When data_type is boolean, only true and false values are allowed. A default option is required: this one will be assigned to your existing tiers. **Response Example** ```json { "data": { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "label": "Discount", "name": "discount", "data_type": "single_select", "options": [] }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Get Perk Retrieve a Perk with its Perk Options using its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/perks/{{perk_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `perk_uuid` *string, required* — The Perk's UUID **Response Example** ```json { "data": { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "label": "Discount", "name": "discount", "data_type": "single_select", "options": [ { "label": "No discount", "value": "0_off", "default": true, }, { "label": "10% off", "value": "10_off", "default": false, }, { "label": "25% off", "value": "25_off", "default": false, } ] }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `110000` | Perk not found. | ## Update Perk Updates an existing Perk. ```http PUT https://api.leat.com/api/v3/oauth/clients/perks/{{perk_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `perk_uuid` *string, required* — The Perk's UUID **Body** - `label` *string, required* — The label of the perk. **Response Example** ```json { "data": { "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839", "label": "Discount", "name": "discount", "data_type": "single_select", "options": [ { "label": "No discount", "value": "0_off", "default": true, }, { "label": "10% off", "value": "10_off", "default": false, }, { "label": "25% off", "value": "25_off", "default": false, } ] }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `110000` | Perk not found. | ## Related - [Tiers](https://docs.leat.com/v3/oauth/tiers.md) — You can offer your Contacts an extra bonus by using Tiers - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created Source: https://docs.leat.com/v3/oauth/perks --- # Portal Sessions A Portal Session is created for a Contact. Portal Sessions support different `scanner_types`, like camera or hand scanner, adding flexibility on how to interact with the loyalty platform. ## Get Portal Session Retrieve a Portal Session by supplying its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/portal-sessions/{{portal_session_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `portal_session_uuid` *string, required* — The UUID of the Portal Session that you want to retrieve. **Response Example** ```json { "data": { "uuid": "d02c7730-a1bb-471d-b293-bdc5add171e5", "url": "https://api.piggy.eu/portal-sessions?uuid=d02c7730-a1bb-471d-b293-bdc5add171e5", "contact": { "uuid": "123" }, "shop": { "id": 243, "uuid": "2222e1242t3324535", "reference": "", "name": "Conch Street" }, "created_at": "2023-10-19T15:11:13+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Create Portal Session When creating a portal session, a valid `shop_uuid` specifying the shop for which the portal session is to be created must be provided. Optionally, a `contact_uuid` can be provided, which will pre-fill the contact's details in the portal session. If no contact UUID is provided, the contact can be added in the portal session itself. It is also possible to specify the `scanner_type` that is being used. This can be either 'camera' or 'hand\_scanner', and defaults to 'camera' if this option is not set. Additionally, it is possible to enable the option for contacts to use their email-address as identification. As well as the option to start a session by registering a new contact. Finally, the portal session can also be configured to use an on-screen keyboard. ```http POST https://api.leat.com/api/v3/oauth/clients/portal-sessions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `shop_uuid` *string, required* — A valid shop UUID for the portal session being created. - `contact_uuid` *string, optional* — The UUID of the Contact for whom the portal session is to be created. This can also be set in the portal session itself. - `scanner_type` *string, optional* — Specify the type of scanner that is being used. Option: 'camera' and 'hand_scanner'. If not set, defaults to 'camera'. - `enable_email_identification` *boolean, optional* — Enable the option for contacts to use their email-address as identification. If not set, defaults to false. - `enable_registration` *boolean, optional* — Enable the option to register new contacts. If not set, defaults to false. - `enable_on_screen_keyboard` *boolean, optional* — Enable the on-screen keyboard. If not set, defaults to false. - `camera_service` *integer, optional* — Select which camera service to use. Options include 1, 2 or 3. Defaults to 3. - `facing_mode` *string, optional* — MediaTrackConstraints facingMode. Specifies the default camera if applicable. Specify 'user' to use the front camera and 'environment' to use the back camera. Defaults to 'user'. - `primary_identification_method` *string, optional* — Start the identification flow with either 'email' or 'identifier'. Defaults to 'identifier'. **Response Example** ```json { "data": { "uuid": "5a2066f3-d52b-4216-8177-ee341dbd9849", "url": "https://api.piggy.eu/portal-sessions?uuid=5a2066f3-d52b-4216-8177-ee341dbd9849", "contact": { "uuid": "123" }, "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "events": [], "created_at": "2023-11-12T19:43:01+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `55031` | Contact not found. | | `1008` | Shop not found. | ## Create Credit Reception Portal Session A portal session can also specifically be made for credit receptions. This is done by specifying the `credit-reception` endpoint. All other parameters are the same as when creating a regular portal session. ```http POST https://api.leat.com/api/v3/oauth/clients/portal-sessions/credit-reception ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `shop_uuid` *string, required* — A valid shop UUID for the portal session being created. - `contact_uuid` *string, optional* — The UUID of the Contact for whom the portal session is to be created. This can also be set in the portal session itself. - `scanner_type` *string, optional* — Specify the type of scanner that is being used. Option: 'camera' and 'hand_scanner'. If not set, defaults to 'camera'. - `enable_email_identification` *boolean, optional* — Enable the option for contacts to use their email-address as identification. If not set, defaults to false. - `enable_registration` *boolean, optional* — Enable the option to register new contacts. If not set, defaults to false. - `enable_on_screen_keyboard` *boolean, optional* — Enable the on-screen keyboard. If not set, defaults to false. - `camera_service` *integer, optional* — Select which camera service to use. Options include 1, 2 or 3. Defaults to 3. - `facing_mode` *string, optional* — MediaTrackConstraints facingMode. Specifies the default camera if applicable. Specify 'user' to use the front camera and 'environment' to use the back camera. Defaults to 'user'. - `primary_identification_method` *string, optional* — Start the identification flow with either 'email' or 'identifier'. Defaults to 'identifier'. **Response Example** ```json { "data": { "uuid": "5a2066f3-d52b-4216-8177-ee341dbd9849", "url": "https://api.piggy.eu/portal-sessions?uuid=5a2066f3-d52b-4216-8177-ee341dbd9849", "contact": { "uuid": "123" }, "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "events": [], "created_at": "2023-11-12T19:43:01+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | | `55031` | Contact not found. | ## Create Redeem Giftcard Portal Session A portal session can also specifically be made for redeeming giftcards. This is done by specifying the `redeem-giftcards` endpoint. This session type requires a purchase amount, `amount_in_cents`, to be provided. It is also possible to specify whether multiple giftcards can be used for a single transaction by supplying the `allow_multiple_giftcards` parameter (defaults to true). ```http POST https://api.leat.com/api/v3/oauth/clients/portal-sessions/redeem-giftcards ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `amount_in_cents` *number, required* — Purchase amount in cents (€10.40 = 1040) - `shop_uuid` *string, required* — A valid shop UUID for the portal session being created. - `allow_multiple_giftcards` *boolean, optional* — Enable the use of multiple giftcards per transaction. If not set, defaults to true. - `contact_uuid` *string, optional* — The UUID of the Contact for whom the portal session is to be created. This can also be set in the portal session itself. - `scanner_type` *string, optional* — Specify the type of scanner that is being used. Option: 'camera' and 'hand_scanner'. If not set, defaults to 'camera'. - `enable_email_identification` *boolean, optional* — Enable the option for contacts to use their email-address as identification. If not set, defaults to false. - `enable_registration` *boolean, optional* — Enable the option to register new contacts. If not set, defaults to false. - `enable_on_screen_keyboard` *boolean, optional* — Enable the on-screen keyboard. If not set, defaults to false. - `camera_service` *integer, optional* — Select which camera service to use. Options include 1, 2 or 3. Defaults to 3. - `facing_mode` *string, optional* — MediaTrackConstraints facingMode. Specifies the default camera if applicable. Specify 'user' to use the front camera and 'environment' to use the back camera. Defaults to 'user'. - `primary_identification_method` *string, optional* — Start the identification flow with either 'email' or 'identifier'. Defaults to 'identifier'. **Response Example** ```json { "data": { "uuid": "5a2066f3-d52b-4216-8177-ee341dbd9849", "url": "https://api.piggy.eu/portal-sessions?uuid=5a2066f3-d52b-4216-8177-ee341dbd9849", "contact": { "uuid": "123" }, "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "events": [], "created_at": "2023-11-12T19:43:01+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | | `55031` | Contact not found. | ## Create Top Up Giftcard Portal Session A portal session can also specifically be made for topping up giftcards. This is done by specifying the `top-up-giftcards` endpoint. All other parameters are the same as when creating a regular portal session. ```http POST https://api.leat.com/api/v3/oauth/clients/portal-sessions/top-up-giftcards ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `shop_uuid` *string, required* — A valid shop UUID for the portal session being created. - `contact_uuid` *string, optional* — The UUID of the Contact for whom the portal session is to be created. This can also be set in the portal session itself. - `scanner_type` *string, optional* — Specify the type of scanner that is being used. Option: 'camera' and 'hand_scanner'. If not set, defaults to 'camera'. - `enable_email_identification` *boolean, optional* — Enable the option for contacts to use their email-address as identification. If not set, defaults to false. - `enable_registration` *boolean, optional* — Enable the option to register new contacts. If not set, defaults to false. - `enable_on_screen_keyboard` *boolean, optional* — Enable the on-screen keyboard. If not set, defaults to false. - `camera_service` *integer, optional* — Select which camera service to use. Options include 1, 2 or 3. Defaults to 3. - `facing_mode` *string, optional* — MediaTrackConstraints facingMode. Specifies the default camera if applicable. Specify 'user' to use the front camera and 'environment' to use the back camera. Defaults to 'user'. - `primary_identification_method` *string, optional* — Start the identification flow with either 'email' or 'identifier'. Defaults to 'identifier'. **Response Example** ```json { "data": { "uuid": "5a2066f3-d52b-4216-8177-ee341dbd9849", "url": "https://api.piggy.eu/portal-sessions?uuid=5a2066f3-d52b-4216-8177-ee341dbd9849", "contact": { "uuid": "123" }, "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "events": [], "created_at": "2023-11-12T19:43:01+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | | `55031` | Contact not found. | ## Create Pay Prepaid Portal Session A portal session can also specifically be made for prepaid transactions. This is done by specifying the `pay-prepaid` endpoint. All other parameters are the same as when creating a regular portal session. ```http POST https://api.leat.com/api/v3/oauth/clients/portal-sessions/pay-prepaid ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `shop_uuid` *string, required* — A valid shop UUID for the portal session being created. - `contact_uuid` *string, optional* — The UUID of the Contact for whom the portal session is to be created. This can also be set in the portal session itself. - `scanner_type` *string, optional* — Specify the type of scanner that is being used. Option: 'camera' and 'hand_scanner'. If not set, defaults to 'camera'. - `enable_email_identification` *boolean, optional* — Enable the option for contacts to use their email-address as identification. If not set, defaults to false. - `enable_registration` *boolean, optional* — Enable the option to register new contacts. If not set, defaults to false. - `enable_on_screen_keyboard` *boolean, optional* — Enable the on-screen keyboard. If not set, defaults to false. - `camera_service` *integer, optional* — Select which camera service to use. Options include 1, 2 or 3. Defaults to 3. - `facing_mode` *string, optional* — MediaTrackConstraints facingMode. Specifies the default camera if applicable. Specify 'user' to use the front camera and 'environment' to use the back camera. Defaults to 'user'. - `primary_identification_method` *string, optional* — Start the identification flow with either 'email' or 'identifier'. Defaults to 'identifier'. **Response Example** ```json { "data": { "uuid": "5a2066f3-d52b-4216-8177-ee341dbd9849", "url": "https://api.piggy.eu/portal-sessions?uuid=5a2066f3-d52b-4216-8177-ee341dbd9849", "contact": { "uuid": "123" }, "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "events": [], "created_at": "2023-11-12T19:43:01+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | | `55031` | Contact not found. | ## Create Top Up Prepaid Portal Session A portal session can also specifically be made to top up a contact's prepaid balance. This is done by specifying the `top-up-prepaid` endpoint. All other parameters are the same as when creating a regular portal session. ```http POST https://api.leat.com/api/v3/oauth/clients/portal-sessions/top-up-prepaid ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `shop_uuid` *string, required* — A valid shop UUID for the portal session being created. - `contact_uuid` *string, optional* — The UUID of the Contact for whom the portal session is to be created. This can also be set in the portal session itself. - `scanner_type` *string, optional* — Specify the type of scanner that is being used. Option: 'camera' and 'hand_scanner'. If not set, defaults to 'camera'. - `enable_email_identification` *boolean, optional* — Enable the option for contacts to use their email-address as identification. If not set, defaults to false. - `enable_registration` *boolean, optional* — Enable the option to register new contacts. If not set, defaults to false. - `enable_on_screen_keyboard` *boolean, optional* — Enable the on-screen keyboard. If not set, defaults to false. - `camera_service` *integer, optional* — Select which camera service to use. Options include 1, 2 or 3. Defaults to 3. - `facing_mode` *string, optional* — MediaTrackConstraints facingMode. Specifies the default camera if applicable. Specify 'user' to use the front camera and 'environment' to use the back camera. Defaults to 'user'. - `primary_identification_method` *string, optional* — Start the identification flow with either 'email' or 'identifier'. Defaults to 'identifier'. **Response Example** ```json { "data": { "uuid": "5a2066f3-d52b-4216-8177-ee341dbd9849", "url": "https://api.piggy.eu/portal-sessions?uuid=5a2066f3-d52b-4216-8177-ee341dbd9849", "contact": { "uuid": "123" }, "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "events": [], "created_at": "2023-11-12T19:43:01+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | | `55031` | Contact not found. | ## Create Voucher Portal Session A portal session can also specifically be made for vouchers. This is done by specifying the `vouchers` endpoint. All other parameters are the same as when creating a regular portal session. ```http POST https://api.leat.com/api/v3/oauth/clients/portal-sessions/vouchers ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `shop_uuid` *string, required* — A valid shop UUID for the portal session being created. - `contact_uuid` *string, optional* — The UUID of the Contact for whom the portal session is to be created. This can also be set in the portal session itself. - `scanner_type` *string, optional* — Specify the type of scanner that is being used. Option: 'camera' and 'hand_scanner'. If not set, defaults to 'camera'. - `enable_email_identification` *boolean, optional* — Enable the option for contacts to use their email-address as identification. If not set, defaults to false. - `enable_registration` *boolean, optional* — Enable the option to register new contacts. If not set, defaults to false. - `enable_on_screen_keyboard` *boolean, optional* — Enable the on-screen keyboard. If not set, defaults to false. - `camera_service` *integer, optional* — Select which camera service to use. Options include 1, 2 or 3. Defaults to 3. - `facing_mode` *string, optional* — MediaTrackConstraints facingMode. Specifies the default camera if applicable. Specify 'user' to use the front camera and 'environment' to use the back camera. Defaults to 'user'. - `primary_identification_method` *string, optional* — Start the identification flow with either 'email' or 'identifier'. Defaults to 'identifier'. **Response Example** ```json { "data": { "uuid": "5a2066f3-d52b-4216-8177-ee341dbd9849", "url": "https://api.piggy.eu/portal-sessions?uuid=5a2066f3-d52b-4216-8177-ee341dbd9849", "contact": { "uuid": "123" }, "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "events": [], "created_at": "2023-11-12T19:43:01+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | | `55031` | Contact not found. | ## Related - [Prepaid Transactions](https://docs.leat.com/v3/oauth/prepaid-transactions.md) — Use prepaid as a payment method using Prepaid Transactions - [Giftcard Transactions](https://docs.leat.com/v3/oauth/giftcard-transactions.md) — Use giftcards as a payment method using Giftcard Transactions Source: https://docs.leat.com/v3/oauth/portal-sessions --- # POS Transactions For the completeness of data in the Piggy system, Piggy Clients may choose to create `POS Transactions` and link these to `Credit Receptions`. ## Create POS Transaction Creates a POS Transaction in the Piggy system. You may choose to set a transaction ID and/or a terminal ID. ```http POST https://api.leat.com/api/v3/oauth/clients/pos-transactions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `amount_in_cents` *number, required* — Purchase amount in cents (€10.40 = 1040) - `transaction_id` *number, optional* — ID for transaction - `terminal_id` *number, optional* — ID of the terminal used for transaction **Response Example** ```json { "data": { "uuid": "1234-abcdefgh-5678-defghijk", "transaction_id": "1", "terminal_id": "1", "created_at": "2021-01-01T12:00:00+00:00", "amount_in_cents": 100 } } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Push POS Transaction Send a push to Device after a POS Transaction creation, so a Credit Reception may be created using the Device. ```http POST https://api.leat.com/api/v3/oauth/clients/pos-transactions/{pos_transaction_uuid}/push ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `pos_transaction_uuid` *string, required* — The POS Transaction UUID **Response Example** ```json { "data": [ ], "meta": [ ] } ``` **Possible errors** | Code | Message | | --- | --- | | `1027` | POS Transaction not found. | | `6001` | The POS transaction is already linked to a credit reception. | ## Related - [Giftcards](https://docs.leat.com/v3/registers/giftcards) — Retrieve all the information stored on Giftcards - [Credit Receptions](https://docs.leat.com/v3/registers/credit-receptions) — One of the most important API calls you're ever gonna need Source: https://docs.leat.com/v3/oauth/pos-transactions --- # Prepaid Transactions Create contact-specific Prepaid Balances to settle refunds or reward extra credits. It works for both on-sight or online stores, allowing quick and easy redemption. The Prepaid Balance of a Contact is incrementally or decrementally updated after a Prepaid Transaction is made for that Contact. The creation of a transaction is validated by supplying the Contact details from whom the transaction is made and by ensuring the transaction is associated with the correct Shop, which is also supplied. ## Create Prepaid Transaction This API call generates a Prepaid Transaction for a specified Contact. Necessary parameters include either the Contact's UUID or their unique identifier value, the amount in cents for the transaction, and the Shop UUID where the transaction is occurring. Using this method ensures and accurate and up-to-date record of the Contact's Prepaid balance. ```http POST https://api.leat.com/api/v3/oauth/clients/prepaid-transactions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_uuid` *string, required_without* — The UUID of the Contact you want to create the Prepaid Transaction for. Required without contact_identifier_value. - `contact_identifier_value` *string, required_without* — The Contact Identifier used to conduct the transaction. Required without contact_uuid. - `amount_in_cents` *integer, required* — The amount for the transaction, supply a round number in cents. - `shop_uuid` *string, required* — The UUID of the Shop where the transaction takes place. **Response Example** ```json { "data": { "uuid": "3cff98cb-644e-4148-bf34-bac842ac602e", "amount_in_cents": -1000, "prepaid_balance": { "balance_in_cents": 4000, "balance": "€40.00" }, "created_at": "2023-11-12T19:49:22+00:00", "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "contact_identifier": { "name": "Krabby Membership", "value": "cilwjefwnsvidjfkjskjf", "active": true } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `60003` | Contact identifier not found. | | `60004` | Contact identifier inactive. | | `1008` | Shop not found. | ## List Prepaid Transactions Returns a paginated list of Prepaid Transactions, which can be filtered either by Contact or Shop. ```http GET https://api.leat.com/api/v3/oauth/clients/prepaid-transactions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, optional* — The Contact's UUID for whom the Prepaid Transactions are to be retrieved. - `shop_uuid` *string, optional* — The Shop's UUID for which the Prepaid Transactions are to be retrieved. - `limit` *string, optional* — Limit of Transactions to be retrieved (max: 30, default: 10). - `page` *string, optional* — Page of Transactions to retrieve (default: 1). **Response Example** ```json { "data": { [ "uuid": "abcde-123-789-uio", "amount_in_cents": -1000, "prepaid_balance": { "balance_in_cents": 4000, "balance": "€40.00" }, "created_at": "2023-11-12T19:49:22+00:00", "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "contact_identifier": { "name": "Krabby Membership", "value": "7890UIOL", "active": true }, "contact": { "uuid": "12345-abcdef-poiuy-0876", "email": "spongebob@bikinibottom.sea" } ], [ "uuid": "asdf-123-uio-789e", "amount_in_cents": 1500, "prepaid_balance": { "balance_in_cents": 1500, "balance": "€15.00" }, "created_at": "2023-11-14T19:49:22+00:00", "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "contact_identifier": { "name": "Krabby Membership", "value": "ABCDE123", "active": true }, "contact": { "uuid": "poiuy-abcdef-12345-0876", "email": "sandy@bikinibottom.sea" } ], [ "uuid": "890-123-uioas-789e", "amount_in_cents": -3400, "prepaid_balance": { "balance_in_cents": 22010, "balance": "€220.10" }, "created_at": "2023-11-19T19:49:22+00:00", "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "contact_identifier": { "name": "Krabby Membership", "value": "UNO768", "active": true }, "contact": { "uuid": "0876-abcdef-12345-abcdef", "email": "squidward@bikinibottom.sea" } ], }, "meta": [ "current_page": 1, "from": 1, "last_page": 1, "per_page": 3, "total": 3 ] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `1008` | Shop not found. | ## Get Prepaid Balance Use this function to retrieve the Prepaid Balance of a particular Contact, identified by their UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}}/prepaid-balance ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID **Response Example** ```json { "data": { "balance_in_cents": 5000, "balance": "€50.00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Reverse Prepaid Transaction At times, an initial Prepaid Transaction needs to be reversed, because of a refund for instance. This will create a new Prepaid Transaction with the same data as initially used, but for the negative amount issued. ```http POST https://api.leat.com/api/v3/oauth/clients/prepaid-transactions/{{prepaid_transaction_uuid}}/reverse ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `prepaid_transaction_uuid` *string, required* — The UUID of the Prepaid Transaction that is to be reversed. **Response Example** ```json { "data": { "uuid": "3cff98cb-644e-4148-bf34-bac842ac602e", "amount_in_cents": -1000, "prepaid_balance": { "balance_in_cents": 4000, "balance": "€40.00" }, "created_at": "2023-11-12T19:49:22+00:00", "shop": { "id": 15, "uuid": "123123", "reference": null, "name": "Krusty Krab" }, "contact_identifier": { "name": "Krabby Membership", "value": "cilwjefwnsvidjfkjskjf", "active": true } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `60003` | Contact identifier not found. | | `60004` | Contact identifier inactive. | | `1008` | Shop not found. | ## Related - [Giftcards](https://docs.leat.com/v3/oauth/giftcards.md) — Retrieve all the information stored on Giftcards - [Giftcard Transactions](https://docs.leat.com/v3/oauth/giftcard-transactions.md) — Use giftcards as a payment method using Giftcard Transactions Source: https://docs.leat.com/v3/oauth/prepaid-transactions --- # Products Products are used for the Line Items of Orders. ## List Products Lists all Products in the Leat system. ```http GET https://api.leat.com/api/v3/oauth/clients/products ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `limit` *number, optional* — The maximum number of Products to return (max 100) - `page` *number, optional* — The page number to return **Response Example** ```json { "data": [ { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Kelp Shake", "description": "A Kelp Shake with extra foam", "categories": [] } ], "meta": { "page": 1, "limit": 10, "viewing_from": 1, "viewing_to": 10, "last_page": 1, "total": 1 } } ``` ## Create Product Creates a new Product in the Leat system and returns the Product entity. ```http POST https://api.leat.com/api/v3/oauth/clients/products ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Product - `description` *string, optional* — The description of the Product - `categories` *array of objects, optional* — List of Categories that the product belongs to - `uuid` *string, optional* — UUID of the Category - `external_identifier` *string, optional* — External identifier of the Category **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Kelp Shake", "description": "A Kelp Shake with extra foam", "categories": [] }, "meta": [] } ``` ## Batch Create Products Creates a batch of Products in the Leat system. Accepts up to 100 Products per request. The maximum allowed payload size is 1MB. ```http POST https://api.leat.com/api/v3/oauth/clients/products/batch ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `products` *array of objects, required* — List of Products - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, required* — The name of the Product - `description` *string, optional* — The description of the Product - `categories` *array of objects, optional* — List of Categories that the product belongs to - `uuid` *string, optional* — UUID of the Category - `external_identifier` *string, optional* — External identifier of the Category **Response Example** ```json { "data": [ "status": "completed" ], "meta": [] } ``` ## Get Product Get a specific Product in the Leat system. ```http GET https://api.leat.com/api/v3/oauth/clients/products/{uuid} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Kelp Shake", "description": "A Kelp Shake with extra foam", "categories": [] }, "meta": [] } ``` ## Find Product Use the `external_identifier` to find a Product. ```http GET https://api.leat.com/api/v3/oauth/clients/products/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Kelp Shake", "description": "A Kelp Shake with extra foam", "categories": [] }, "meta": [] } ``` ## Find or Create Product Finds an existing Product or creates a new Product in the Leat system and returns the Product entity. ```http POST https://api.leat.com/api/v3/oauth/clients/products/find-or-create ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — A unique identifier from your system - `name` *string, optional* — The name of the Product - `description` *string, optional* — The description of the Product - `categories` *array of objects, optional* — List of Categories that the product belongs to - `uuid` *string, optional* — UUID of the Category - `external_identifier` *string, optional* — External identifier of the Category **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Kelp Shake", "description": "A Kelp Shake with extra foam", "categories": [] }, "meta": [] } ``` ## Update Product Updates a Product in the Leat system and returns the updated Product entity. ```http PUT https://api.leat.com/api/v3/oauth/clients/products/{uuid} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, optional* — A unique identifier from your system - `name` *string, optional* — The name of the Product - `description` *string, optional* — The description of the Product - `categories` *array of objects, optional* — List of Categories that the product belongs to - `uuid` *string, optional* — UUID of the Category - `external_identifier` *string, optional* — External identifier of the Category **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "external_identifier": "A-12345-B", "name": "Kelp Shake", "description": "A Kelp Shake with extra foam", "categories": [] }, "meta": [] } ``` ## Delete Product Delete a specific Product in the Leat system. ```http DELETE https://api.leat.com/api/v3/oauth/clients/products/{uuid} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": null, "meta": [] } ``` ## Related - [Orders](https://docs.leat.com/v3/oauth/orders.md) — Create or refund Orders - [Categories](https://docs.leat.com/v3/oauth/categories.md) — Create, view or update Categories Source: https://docs.leat.com/v3/oauth/products --- # Promotion Attributes Promotion Attributes are customizable fields that you can use to add unique characteristics to your Promotions. Tailor these attributes to align with your own business logic. ## List Promotion Attributes Retrieve a list of all Promotion Attributes associated with your Account. ```http GET https://api.leat.com/api/v3/oauth/clients/promotion-attributes ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "id": 2238, "name": "shipping", "label": "Shipping", "type": "select", "description": "", "position": 1, "options": [ { "value": "domestic", "label": "Domestic", "description": null, "media": null }, { "value": "international", "label": "International", "description": null, "media": null }, { "value": "intergalactic", "label": "Intergalactic", "description": null, "media": null } ], "group_name": null, "created_at": "2023-11-22T11:46:25+00:00", "last_used_date": null, "created_by_user": null, "meta": [] }, { "id": 2239, "name": "shipping2", "label": "shipping2", "type": "select", "description": "", "position": 2, "options": [ { "value": "domestic", "label": "Domestic", "description": null, "media": null }, { "value": "international", "label": "International", "description": null, "media": null }, { "value": "intergalactic", "label": "Intergalactic", "description": null, "media": null } ], "group_name": null, "created_at": "2023-11-22T11:49:53+00:00", "last_used_date": null, "created_by_user": null, "meta": [] }, { "id": 2242, "name": "some_random_attribute_3", "label": "Some Random Attribute 3", "type": "text", "description": "", "position": 3, "options": [], "group_name": null, "created_at": "2023-11-22T14:42:29+00:00", "last_used_date": null, "created_by_user": null, "meta": [] } ], "meta": [] } ``` ## Create Promotion Attribute Globally create a Promotion Attribute for the Account in question, customizable to your wishes. ```http POST https://api.leat.com/api/v3/oauth/clients/promotion-attributes ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — The (internal) name for the attribute. - `label` *string, required* — The label for the attribute. - `type` *string, required* — Type of attribute (options: url, text,date, phone, float, color, email, number, select, boolean, rich_text, date_time, long_text, date_range, time_range, identifier, birth_date, file_upload, media_upload, multi_select, license_plate) - `description` *string, optional* — The description of the attribute. - `options` *array, optional* — Applicable for (multi)selects only. Array consisting of label and value objects. **Response Example** ```json { "data": { "name": "shipping", "label": "Shipping", "type": "select", "description": "", "position": 1, "options": [ { "value": "domestic", "label": "Domestic", "description": null, "media": null }, { "value": "international", "label": "International", "description": null, "media": null }, { "value": "intergalactic", "label": "Intergalactic", "description": null, "media": null } ], "group_name": null, "created_at": "2023-11-22T11:46:25+00:00", "last_used_date": null, "created_by_user": null, "meta": [] }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `60009` | Attribute already exists. | ## Related - [Promotions](https://docs.leat.com/v3/oauth/promotions.md) — Customize your Promotions and fully integrate with your software - [Vouchers](https://docs.leat.com/v3/oauth/vouchers.md) — Create Vouchers for Promotions for the Contacts to use Source: https://docs.leat.com/v3/oauth/promotion-attributes --- # Promotions Vouchers are directly connected to specific Promotions, just like Giftcards are connected to Giftcard Programs. Each Promotion can have its own `name`and `description`which show up for the Contact when they get a Voucher. Also, Promotions can be set up with different rules and ways to make Vouchers. This makes it easy to fit Promotions to your business and marketing needs. ## List Promotions Retrieves a list of all Promotions linked to an Account, providing detailed insights into each promotion's attributes such as name, description, voucher limits, and expiration periods. ```http GET https://api.leat.com/api/v3/oauth/clients/promotions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "attributes": { "acme_promotion_id": "ab123" }, "name": "Free Krabby Patty", "description": "The freshest patty in the seven seas", "voucher_limit": 5000, "limit_per_contact": null, "expiration_duration": null, "uuid": "123", "media": { "type": "image", "value": "https://example.com/image.jpg" } }, { "attributes": { "acme_promotion_id": "ab123" }, "name": "Free Kelp Smoothie", "description": "A refreshing drink for your summer adventures", "voucher_limit": null, "limit_per_contact": null, "expiration_duration": null, "uuid": "983a8692-f9de-4aa5-9fde-138d5f8f153b", "media": null }, { "attributes": { "acme_promotion_id": "ab123" }, "name": "Barnacle Bites", "description": "A delicious snack", "voucher_limit": null, "limit_per_contact": null, "expiration_duration": null, "uuid": "9c966b16-6212-4169-ae2f-a523566b533f", "media": { "type": "image", "value": "https://example.com/image.jpg" } }, ], "meta": [] } ``` ## Get Promotion Retrieve a specific Promotion by specifying its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/promotions/{{promotion_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `promotion_uuid` *string, required* — The UUID of the Promotion you want to retrieve. **Response Example** ```json { "data": { "attributes": { "attributeOne": null, "attributeTwo": null }, "name": "Jellyfishing Day Special", "description": "A special promotion for Jellyfishing Day", "voucher_limit": 12, "limit_per_contact": 3, "expiration_duration": 86400, "uuid": "5c3d59c7-7799-4d41-894a-8f46e50edfda", "media": { "type": "image", "value": "https://example.com/image.jpg" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `80002` | Promotion not found. | ## Related - [Vouchers](https://docs.leat.com/v3/oauth/vouchers.md) — Create Vouchers for Promotions for the Contacts to use - [Promotion Attributes](https://docs.leat.com/v3/oauth/promotion-attributes.md) — Customize your Promotions and fully integrate with your software Source: https://docs.leat.com/v3/oauth/promotions --- # Referrals Get insights in your referral program and retrieve all Referrals made by your Contact. The referring Contact is the one who shares their unique Referral code, enabling other contacts to sign up using that code. The referred Contact is the Contact that has used a unique Referral code to sign up. ## List Referrals Retrieve a list of Referrals from your referral program. ```http GET https://api.leat.com/api/v3/oauth/clients/referrals ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `referring_contact_uuid` *string, optional* — The uuid of the Contact who has referred another Contact. - `status` *string, optional* — The Referral status can be one of the following: pending, completed, or failed, indicating the current state of the Referral. - `limit` *number, optional* — Maximum number of items to retrieve. Default value = 30, limit max = 100. - `page` *number, optional* — Page within list to retrieve **Response Example** ```json { "data": [ { "uuid": "d1fd45ac-0993-46c1-9ae7-334bcd9b0ad7", "referring_contact": { "uuid": "2e70ea71-631f-4b3c-8097-8e0284751c96", "email": "patrick1@bikinibottom.sea" }, "referred_contact": { "uuid": "435438ef-7e62-43aa-87c7-3f6d3deb0a72", "email": "gary1@bikinibottom.sea", } "status": "completed" }, { "uuid": "d1fd45ac-0993-46c1-9ae7-334bcd9b0ad7", "referring_contact": { "uuid": "8ff157c6-9808-46b9-ac43-ae14809d8f81", "email": "patrick2@bikinibottom.sea" }, "referred_contact": { "uuid": "3ba05ab0-28f6-465d-a345-aaeccd83a24f", "email": "gary2@bikinibottom.sea", } "status": "completed" }, { "uuid": "d1fd45ac-0993-46c1-9ae7-334bcd9b0ad7", "referring_contact": { "uuid": "2e70ea71-631f-4b3c-8097-8e0284751c96", "email": "patrick3@bikinibottom.sea" }, "referring_contact": { "uuid": "5bc55776-dfac-41b4-a257-0aa2d2dac7b5", "email": "gary3@bikinibottom.sea", } "status": "completed" } ], "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Get Referral Program Settings Retrieve a Referral program's settings, like the incentive for both the *referring* contact and the *referred* contact. ```http GET https://api.leat.com/api/v3/oauth/clients/referrals/settings ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "completion_event": { "type": "loyalty_transaction", "data": { "number_of_transactions": 5 }, }, "limit_per_contact": 10, "referred_contact_incentive": { "type": "loyalty_points", "data": { "points": 50, "business_profile": { "uuid": "13ac4cc1-3522-4dfc-ae4a-a821e5090108", "name": "Coral Avenue" }, }, }, "referring_contact_incentive": { "type": "voucher", "data": { "promotion": { "uuid": "539483d0-fab4-40db-add5-2ec65ef12901", "name": "Free Kelp Juice" }, }, }, }, "meta": {} } ``` Source: https://docs.leat.com/v3/oauth/referrals --- > **Warning** > > **Please note:** the *Reviews* API is now in beta. Some functionalities may not yet be available at that time. Please note that the API may be subject to change. # List Reviews Retrieve a paginated list of Reviews, ordered by most recent. ```http GET https://api.leat.com/api/v3/oauth/clients/reviews ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `limit` *number, optional* — Number of Reviews to retrieve (min: 0, max: 500, default: 30) - `page` *number, optional* — Number of page to retrieve (default: 1) - `contact` *object, optional* — Only return Reviews written by this Contact - `uuid` *string, required* — UUID of the Contact - `business_profile` *object, optional* — Only return Reviews that came in through this Business Profile - `uuid` *string, required* — UUID of the Business Profile - `product` *object, optional* — Only return Reviews about this product - `external_identifier` *string, required* — The product's identifier in the source system - `type` *string, optional* — Filter by what was reviewed Possible values: - `COMPANY` — The Review is about the company as a whole - `PRODUCT` — The Review is about a single product **Response Example** ```json { "data": [ { "uuid": "87824d97-a0a0-4764-9cd2-9df88579fd47", "contact": { "uuid": "6e8ec5a8-0eb7-4e5f-ad20-c0d271d97d92", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "9c1b2d61-1f8e-4a2c-9a77-2c0c1d5f77aa", "name": "Krusty Krab" }, "product": null, "type": "COMPANY", "title": "Best patty in the sea", "body": "Served in under two minutes and still perfect.", "rating": 5, "submitted_at": "2024-05-01T20:00:00+00:00", "external_identifier": "judgeme-111", "source": "judge.me", "custom_attributes": [], "created_at": "2024-05-01T20:04:12+00:00" }, { "uuid": "af0078fb-39d4-4ffe-a9a8-2b86edd6f71e", "contact": { "uuid": "3da1b8ed-161e-4733-909d-fc3e65205be3", "email": "mrkrabs@bikinibottom.sea" }, "business_profile": null, "product": { "external_identifier": "kelp-shake-500ml", "name": "Kelp Shake" }, "type": "PRODUCT", "title": null, "body": "The kelp shake is an acquired taste.", "rating": 2, "submitted_at": "2024-04-28T11:22:03+00:00", "external_identifier": "reviewsio-8817", "source": "reviews.io", "custom_attributes": [], "created_at": "2024-04-28T11:25:41+00:00" } ], "meta": { "current_page": 1, "last_page": 1, "per_page": 30, "total": 2 } } ``` # Find Review Use the `external_identifier` to find a Review. ```http GET https://api.leat.com/api/v3/oauth/clients/reviews/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `external_identifier` *string, required* — The Review's identifier in the source system **Response Example** ```json { "data": { "uuid": "1e32b995-b339-4da8-93a1-f2783b2c80c4", "contact": { "uuid": "6e8ec5a8-0eb7-4e5f-ad20-c0d271d97d92", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "9c1b2d61-1f8e-4a2c-9a77-2c0c1d5f77aa", "name": "Krusty Krab" }, "product": null, "type": "COMPANY", "title": "Best patty in the sea", "body": "Served in under two minutes and still perfect.", "rating": 5, "submitted_at": "2024-05-01T20:00:00+00:00", "external_identifier": "judgeme-111", "source": "judge.me", "custom_attributes": [], "created_at": "2024-05-01T20:04:12+00:00" }, "meta": [] } ``` # Create Review Create a new Review in the Leat system and returns the Review entity. ```http POST https://api.leat.com/api/v3/oauth/clients/reviews ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `contact` *object, required* — Contact who wrote the Review - `uuid` *string, required* — UUID of the Contact - `business_profile` *object, optional* — Business Profile the Review came in through - `uuid` *string, required* — UUID of the Business Profile - `type` *string, required* — What was reviewed Possible values: - `COMPANY` — The Review is about the company as a whole - `PRODUCT` — The Review is about a single product - `rating` *number, required* — The rating given, from 1 to 5 - `external_identifier` *string, required* — The Review's identifier in the source system. Unique per account, so replaying the same review is rejected instead of duplicated (max 255 characters) - `title` *string, optional* — The title of the Review (max 255 characters) - `body` *string, optional* — The text of the Review - `product` *object, required if type is PRODUCT* — The product the Review is about. Prohibited when the type is COMPANY - `external_identifier` *string, required* — The product's identifier in the source system (max 255 characters) - `name` *string, optional* — Name of the product. Supplying it creates the product when the external identifier is not known yet; without it, an unknown product is rejected (max 255 characters) - `submitted_at` *string, required* — The date and time the Review was submitted in the source system (in ISO 8601 format) - `source` *string, optional* — Source from where the Review was created (max 255 characters) - `custom_attributes` *array, optional* — Custom attribute values to store on the Review **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "15ead936-0d0a-40ed-877b-39f22b34be53", "email": "spongebob@bikinibottom.sea" }, "business_profile": null, "product": { "external_identifier": "krabby-patty", "name": "Krabby Patty" }, "type": "PRODUCT", "title": "Krabby Patty, again", "body": "Four out of five. Would eat daily.", "rating": 4, "submitted_at": "2024-05-01T20:00:00+00:00", "external_identifier": "judgeme-112", "source": "judge.me", "custom_attributes": { "would_recommend": "1" }, "created_at": "2024-05-01T20:04:12+00:00" }, "meta": [] } ``` # Update Review Updates the Review with the given external identifier. Only its rating, title, body and custom attributes can change. Everything else is immutable: the Contact, the Business Profile, the type, the product, the external identifier and the submitted date cannot be changed. ```http PUT https://api.leat.com/api/v3/oauth/clients/reviews/{{external_identifier}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `rating` *number, optional* — The rating given, from 1 to 5 - `title` *string, optional* — The title of the Review (max 255 characters) - `body` *string, optional* — The text of the Review - `custom_attributes` *array, optional* — Custom attribute values to store on the Review **Response Example** ```json { "data": { "uuid": "1e32b995-b339-4da8-93a1-f2783b2c80c4", "contact": { "uuid": "6e8ec5a8-0eb7-4e5f-ad20-c0d271d97d92", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "9c1b2d61-1f8e-4a2c-9a77-2c0c1d5f77aa", "name": "Krusty Krab" }, "product": null, "type": "COMPANY", "title": "Best patty in the sea", "body": "Served in under two minutes and still perfect.", "rating": 5, "submitted_at": "2024-05-01T20:00:00+00:00", "external_identifier": "judgeme-111", "source": "judge.me", "custom_attributes": [], "created_at": "2024-05-01T20:04:12+00:00" }, "meta": [] } ``` # Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Every Review belongs to a Contact - [Business Profiles](https://docs.leat.com/v3/oauth/shops.md) — The locations a Review can be written about Source: https://docs.leat.com/v3/oauth/reviews --- # Reward Receptions Reward Receptions allow end-users to redeem Rewards using the credits they've accumulated in a Loyalty Program. These Rewards can range from products, services, or any other special offers provided by the business. ## Create Reward Reception Create a Reward Reception by specifying the Contact's UUID and the desired Reward's UUID. It enables a Contact to claim a Reward. Our backend manages various scenarios, like insufficient credits, stock limitations, Reward limits, and inactivity of Rewards, ensuring a correct Reward claiming process. A Reward can be limited in two ways. Its limit per contact is how many times one Contact can claim it in total, over the Contact's whole lifetime: there is no per-day or per-week window, and a reversed Reward Reception doesn't count. When it is reached, the request fails with `1019`. Its stock is shared by all Contacts: for a physical Reward the stock set on the Reward, for a digital Reward the number of unused codes. When it runs out, the request fails with `1018`. **Important:** Our own Piggy products require some sort of validation to ensure the user is the owner of the email address for reward claiming (either through the loyalty card – which needs to be activated by email address – or by logging in). If you intend to have Contacts claim rewards by finding their ID's through their email address, please make sure they're authenticated in some way. ```http POST https://api.leat.com/api/v3/oauth/clients/reward-receptions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_uuid` *string, required* — The UUID of the Contact you want to create the Reward Reception for. - `reward_uuid` *string, required* — The Reward's UUID that is related to the reception. - `contact_identifier_value` *string, optional* — The Contact Identifier used by the Contact. - `shop_uuid` *string, required* — The UUID of the Shop you want to create the Reward Reception for. **Response Example** ```json { "data": { "title": "Krabby Patty", "type": "reward_reception", "uuid": "55537e6c-23e8-4c0c-bbce-65f587fb543a", "credits": -50, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 250 } }, "shop": { "id": 15, "uuid": "123123", "type": "physical", "name": "Krusty Krab", "address": { "id": 59, "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "contact_identifier": null, "created_at": "2023-11-23T15:27:26+00:00", "reward": { "id": 20002, "uuid": "539483d0-fab4-40db-add5-2ec65ef12901", "title": "Krabby Patty", "description": "Kelp Juice or Sea Water", "reward_type": "PHYSICAL", "media": { "type": "image", "value": "http://images.google.com/image/krabby-patty/300x200.jpg" }, "required_credits": 50, "pre_redeemable": true, "expiration_duration": 6307200, "is_active": true, "cost_price": null, "stock": null }, "expires_at": "2024-02-04T15:27:26+00:00", "has_been_collected": false }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `1008` | Shop not found. | | `6003` | Reward not found. | | `1017` | Onvoldoende punten | Insufficient credits. | | `1018` | The stock on this reward is empty. | | `1019` | You have reached your claim limit on this reward. | | `1052` | Reward is inactive. | ## Reverse Reward Reception At times, a Reward redemption needs to be reversed, because of a canceled order for instance. This will create a new Credit Reception with the same data as initially used, to reissue the credits used. The reversed Reward Reception no longer counts towards the Contact's limit on that Reward, so they can claim it again. A physical Reward with a limited stock gets one back in stock. A digital Reward's code is not released, because the Contact has already received it. ```http POST https://api.leat.com/api/v3/oauth/clients/reward-receptions/{{reward_reception_uuid}}/reverse ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `reward_reception_uuid` *string, required* — The UUID of the Reward Reception that needs to be reversed **Response Example** ```json { "data": { "type": "credit_reception", "contact_identifier": null, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea", "credit_balance": { "id": 2625740, "balance": 1642 } }, "shop": { "uuid": "abc2-3456-ask03l-9dflas", "type": "physical", "name": "Krusty Krab", "address": { "shop_id": 15, "street": "Conch Street", "housenumber": 12, "postalcode": "9999YZ", "city": "Bikini Bottom", "state": "NH", "country": "NL", "latitude": null, "longitude": null, "house_number": null, "postal_code": null } }, "credits": 10, "created_at": "2023-11-06T09:12:32+00:00", "uuid": "294183c2-20f2-4f86-a5e5-17e71c4b0660", "unit": { "name": "purchase_amount", "label": "Purchase amount", "prefix": "€", "is_default": true }, "unit_value": null, }, "meta": {} } } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Get information and create contacts using our public API - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards Source: https://docs.leat.com/v3/oauth/reward-receptions --- # Rewards Once end-users have saved up enough credits at a certain Loyalty Program, they can use those credits to redeem Rewards. These Rewards can be accessed through our API, which allows listing of available Rewards at a specific Shop or for a specific Contact. If claimable Rewards are found for a Contact, they can be redeemed through Reward Receptions. ## List Rewards This API endpoint is used to list Rewards available for to be shown in-store or to list Rewards that a specific Contact can claim, with potentially a subsequent Create Reward Reception call when a Contact claims a Reward. The list of Rewards for a particular Contact might differ from the full list of Rewards. ```http GET https://api.leat.com/api/v3/oauth/clients/rewards ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `shop_uuid` *string, optional* — The Shop UUID for which the Rewards you want to retrieve. - `contact_uuid` *string, optional* — List the claimable rewards associated with the Contact by supplying their UUID. **Response Example** ```json { "data": [ { "uuid": "6bfe1e50-b13a-49ab-85c9-98cd8b734da2", "title": "Jellyfish Net", "type": "DIGITAL", "is_active": true, "reward_type": "DIGITAL", "description": "Help save the jellyfish", "required_credits": 25, "media": { "type": "image", "value": "https://images.unsplash.com/photo-1565945887714-d5139f4eb0ce" }, "custom_attribute_one": "ABC", "custom_attribute_two": "123" }, { "uuid": "bcd51b4b-88be-4a5d-b658-0e59dc1dabbc", "title": "Krabby Patty", "type": "PHYSICAL", "is_active": true, "reward_type": "PHYSICAL", "description": "Choice between Kelp Shake, Coral Bits or Barnacle Chips", "required_credits": 50, "media": { "type": "image", "value": "https://images.unsplash.com/photo-1565945887714-d5139f4eb0ce" }, "custom_attribute_one": "DEF", "custom_attribute_two": "456", "pre_redeemable": false, "expiration_duration": null }, { "uuid": "539483d0-fab4-40db-add5-2ec65ef12901", "title": "Kelp Shake Deluxe", "type": "PHYSICAL", "is_active": true, "reward_type": "PHYSICAL", "description": "Choice between Kelp Fries, Coral Shake or Seaweed Salad", "required_credits": 75, "media": { "type": "image", "value": "https://images.unsplash.com/photo-1565945887714-d5139f4eb0ce" }, "custom_attribute_one": "GHI", "custom_attribute_two": "789", "pre_redeemable": true, "expiration_duration": 6307200 }, { "uuid": "31909cd8-a078-49a9-a7e1-aa95aa9e8c4d", "title": "Krusty Krab Hat", "type": "PHYSICAL", "is_active": true, "reward_type": "PHYSICAL", "description": "Size: Small Fry/Regular/Krabby/Super-Sized", "required_credits": 150, "media": { "type": "image", "value": "https://images.unsplash.com/photo-1565945887714-d5139f4eb0ce" }, "custom_attribute_one": "JKL", "custom_attribute_two": "012", "pre_redeemable": false, "expiration_duration": null } ], "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | | `55031` | Contact not found. | ## Get Reward Retrieve a specific Reward by specifying its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/rewards/{{reward_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `reward_uuid` *string, required* — The UUID of the Reward you want to retrieve. **Response Example** ```json { "data": { "uuid": "539483d0-fab4-40db-add5-2ec65ef12901", "title": "Krabby Patty", "description": null, "cost_price": null, "required_credits": 2, "media": { "type": "image", "value": "https://example.com/image.jpg" }, "is_active": true, "active": true, "reward_type": "PHYSICAL", "stock": null, "example_attribute": "custom attributes for rewards are included like this" }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `6003` | Reward not found. | ## Update Reward You can modify both global values and custom Reward Attributes. If you need to update a custom attribute—one that you've created specifically for a Reward—simply include it in the parameters, just as you would with the title, description, and required credits. ```http PUT https://api.leat.com/api/v3/oauth/clients/rewards/{{reward_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `title` *string, optional* — A new title for the Reward. - `description` *string, optional* — A new description for the Reward. - `required_credits` *number, optional* — Number of credits required to claim this Reward. **Response Example** ```json { "data": { "uuid": "539483d0-fab4-40db-add5-2ec65ef12901", "title": "Krabby Patty", "description": null, "cost_price": null, "required_credits": 2, "media": { "type": "image", "value": "https://example.com/image.jpg" }, "is_active": true, "active": true, "reward_type": "PHYSICAL", "stock": null, }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `6003` | Reward not found. | ## Related - [Collectable Rewards](https://docs.leat.com/v3/oauth/collectable-rewards.md) — Physical Rewards that have been claimed but not yet collected. - [Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md) — Create reward receptions for end-users who saved up enough credits. Source: https://docs.leat.com/v3/oauth/rewards --- # Shift Teams Shift Teams are replicas of the teams in your own scheduling system: the group a Shift is worked by. A Team is a name here, which a Shift can be assigned to. > **Warning** > > **Please note:** the *Shift Teams* API is now in beta. Some functionalities may not yet be available at that time. Please note that the API may be subject to change. ## List Teams Returns a paginated list of Teams for Shifts. ```http GET https://api.leat.com/api/v3/oauth/clients/shift-teams ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `page` *integer, optional* — The page number. - `limit` *integer, optional* — The maximum number of Teams to retrieve (default: 30, max: 100). **Response Example** ```json { "data": [ { "uuid": "b5701f1b-5e31-49b6-b94f-2f388e72730a", "name": "Fry Cook Crew", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T09:00:00+00:00", }, { "uuid": "90597ad4-a00e-4d3c-aca0-3ca6ec222f23", "name": "Housekeeping", "created_at": "2024-12-01T09:00:00+00:00", "updated_at": "2024-12-08T09:00:00+00:00", }, ], "meta": { "page": 1, "per_page": 30, "last_page": 10, "total": 300 } } ``` ## Create Team Creates a new Team for Shifts. ```http POST https://api.leat.com/api/v3/oauth/clients/shift-teams ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — The Name of the Team you want to create. **Response Example** ```json { "data": { "uuid": "b91e16b3-f4bc-4cdb-a2ef-68b596b3ce0f", "name": "Management", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Find Team by Name Returns all relevant information for a Team. ```http GET https://api.leat.com/api/v3/oauth/clients/shift-teams/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — The Name of this Team. **Response Example** ```json { "data": { "uuid": "b91e16b3-f4bc-4cdb-a2ef-68b596b3ce0f", "name": "Management", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `80540` | Shift team not found. | ## Get Team Retrieve a Team using its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/shift-teams/{{uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `uuid` *string, required* — The UUID for this Team. **Response Example** ```json { "data": { "uuid": "b91e16b3-f4bc-4cdb-a2ef-68b596b3ce0f", "name": "Management", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `80540` | Shift team not found. | ## Update Team Updates the attributes of a Team. ```http PUT https://api.leat.com/api/v3/oauth/clients/shift-teams/{{uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `uuid` *string, required* — The UUID for this Team. **Body** - `name` *string, required* — The Name for this Team. **Response Example** ```json { "data": { "uuid": "b91e16b3-f4bc-4cdb-a2ef-68b596b3ce0f", "name": "Management", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80540` | Shift team not found. | ## Related - [Shifts](https://docs.leat.com/v3/oauth/shifts.md) — List, Update or Create Shifts - [Shift Types](https://docs.leat.com/v3/oauth/shift-types.md) — Create or Update Shift Types Source: https://docs.leat.com/v3/oauth/shift-teams --- # Shift Types Shift Types are replicas of the shift kinds in your own scheduling system, so the Shifts of one kind can be grouped and reported on together. > **Warning** > > **Please note:** the *Shift Types* API is now in beta. Some functionalities may not yet be available at that time. Please note that the API may be subject to change. ## List Shift Types Returns a paginated list of Shift Types. ```http GET https://api.leat.com/api/v3/oauth/clients/shift-types ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `page` *integer, optional* — The page number. - `limit` *integer, optional* — The maximum number of Shift Types to retrieve (default: 30, max: 100). **Response Example** ```json { "data": [ { "uuid": "8880c45e-36ef-4ef5-ac10-8dd428260907", "name": "Night", "description": "Shifts between 23:00 and 06:00.", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, { "uuid": "ec92b3cd-d896-43ac-ae8c-c8eafeb90288", "name": "Weekend", "description": "Shifts on Saturday or Sunday.", "created_at": "2024-12-01T09:00:00+00:00", "updated_at": "2024-12-08T09:00:00+00:00", }, ], "meta": { "page": 1, "per_page": 30, "last_page": 10, "total": 300 } } ``` ## Create Shift Type Creates a new Shift Type. ```http POST https://api.leat.com/api/v3/oauth/clients/shift-types ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — The Name of the Shift Type you want to create. - `description` *string, required* — The Description of the Shift Type you want to create. **Response Example** ```json { "data": { "uuid": "8880c45e-36ef-4ef5-ac10-8dd428260907", "name": "Night", "description": "Shifts between 23:00 and 06:00", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Find Shift Type by Name Returns all relevant information for a Shift Type. ```http GET https://api.leat.com/api/v3/oauth/clients/shift-types/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — The Name of this Shift Type. **Response Example** ```json { "data": { "uuid": "8880c45e-36ef-4ef5-ac10-8dd428260907", "name": "Night", "description": "Shifts between 23:00 and 06:00", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `80550` | Shift type not found. | ## Get Shift Type Retrieve a Shift Type using its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/shift-types/{{uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `uuid` *string, required* — The UUID for this Shift Type. **Response Example** ```json { "data": { "uuid": "8880c45e-36ef-4ef5-ac10-8dd428260907", "name": "Night", "description": "Shifts between 23:00 and 06:00", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `80550` | Shift type not found. | ## Update Shift Type Updates the attributes of a Shift Type. ```http PUT https://api.leat.com/api/v3/oauth/clients/shift-types/{{uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `uuid` *string, required* — The UUID for this Shift Type. **Body** - `name` *string, required* — The Name for this Shift Type. - `description` *string, required* — The Description for this Shift Type. **Response Example** ```json { "data": { "uuid": "8880c45e-36ef-4ef5-ac10-8dd428260907", "name": "Night", "description": "Shifts between 23:00 and 06:00", "created_at": "2024-12-08T09:00:00+00:00", "updated_at": "2024-12-08T17:30:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80550` | Shift type not found. | ## Related - [Shifts](https://docs.leat.com/v3/oauth/shifts.md) — List, Update or Create Shifts - [Teams](https://docs.leat.com/v3/oauth/shift-teams.md) — Create or Update Teams for Shifts Source: https://docs.leat.com/v3/oauth/shift-types --- # Shifts Shifts are replicas of the shifts planned in your own scheduling system. Leat holds enough of one to tie it to a Contact and a Shop, optionally through a Shift Team and a Shift Type; your system stays the record the roster is built in. > **Warning** > > **Please note:** the *Shifts* API is now in beta. Some functionalities may not yet be available at that time. Please note that the API may be subject to change. ## List Shifts Returns a paginated list of Shifts. ```http GET https://api.leat.com/api/v3/oauth/clients/shifts ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `shop_uuid` *string, required* — The UUID of the Shop you want to create the Shift for. - `status` *string, required* — The Status for this Shift. - `team_uuid` *string, optional* — The UUID of the Team you want to create the Shift for. - `page` *integer, optional* — The page number. - `limit` *integer, optional* — The maximum number of Shifts to retrieve (default: 30, max: 100). **Response Example** ```json { "data": [ { "uuid": "69fa84f5-1270-4479-a9f9-75737d6739bf", "contact": [ 'uuid': "33630ed6-fd17-455a-b1ed-7494c3503ab3", 'email': "spongebob@bikinibottom.sea", ], "external_id": "ABC-123", "team_uuid": "d37b1e30-23a2-40b6-ad4c-fa30ab0e9aa7", "shift_type_uuid": "1d1bf619-b097-4461-96a2-f8e7a5249519", "status": "approved", "break_in_minutes": 30, "net_duration_in_minutes": 480, "started_at": "2024-12-01T09:00:00+00:00", "ended_at": "2024-12-01T17:30:00+00:00", "created_at": "2024-12-08T09:00:00+00:00", }, { "uuid": "bf7e6de4-13d7-444d-bf90-a4b68cb71f11", "contact": { "uuid": "bf12c2bb-8ba9-49fd-aa76-a7aa35f07280", "email": "sandy@bikinibottom.sea" }, "external_id": "XYZ-789", "team_uuid": "4e13e25d-74da-443c-adb6-3826199de000", "shift_type_uuid": "b5701f1b-5e31-49b6-b94f-2f388e72730a", "status": "pending", "break_in_minutes": 45, "net_duration_in_minutes": 420, "started_at": "2024-12-02T08:00:00+00:00", "ended_at": "2024-12-02T15:30:00+00:00", "created_at": "2024-12-08T09:15:00+00:00" }, ], "meta": { "page": 1, "per_page": 30, "last_page": 10, "total": 300 } } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | | `80531` | Shift status not found. | | `80540` | Shift team not found. | ## Create Shift Creates a new Shift. ```http POST https://api.leat.com/api/v3/oauth/clients/shifts ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `contact_uuid` *string, required* — The UUID of the Contact you want to create the Shift for. - `shop_uuid` *string, required* — The UUID of the Shop you want to create the Shift for. - `started_at` *string, required* — The start date and time of the Shift in ISO 8601 format. - `ended_at` *string, required* — The end date and time of the Shift in ISO 8601 format. - `status` *string, required* — The Status for this Shift. - `external_id` *string, optional* — An external identifier for the Shift. - `team_uuid` *string, optional* — The UUID of the Team you want to create the Shift for. - `shift_type_uuid` *string, optional* — The UUID of the Shift Team you want to create the Shift for. - `break_in_minutes` *integer, optional* — The break in a minutes for this Shift. **Response Example** ```json { "data": { "uuid": "69fa84f5-1270-4479-a9f9-75737d6739bf", "contact": [ 'uuid': "33630ed6-fd17-455a-b1ed-7494c3503ab3", 'email': "spongebob@bikinibottom.sea", ], "external_id": "ABC-123", "team_uuid": "d37b1e30-23a2-40b6-ad4c-fa30ab0e9aa7", "shift_type_uuid": "1d1bf619-b097-4461-96a2-f8e7a5249519", "status": "approved", "break_in_minutes": 30, "net_duration_in_minutes": 480, "started_at": "2024-12-01T09:00:00+00:00", "ended_at": "2024-12-01T17:30:00+00:00", "created_at": "2024-12-08T09:00:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | | `1008` | Shop not found. | | `80530` | Shift not found. | | `80531` | Shift status not found. | | `80540` | Shift team not found. | | `80550` | Shift type not found. | | `80531` | Shift with external id already exists for this account. | ## Find Shift by External ID Returns all relevant information for a Shift. ```http GET https://api.leat.com/api/v3/oauth/clients/shifts/find ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `external_id` *string, required* — The external identifier for this Shift. **Response Example** ```json { "data": { "uuid": "69fa84f5-1270-4479-a9f9-75737d6739bf", "contact": [ 'uuid': "33630ed6-fd17-455a-b1ed-7494c3503ab3", 'email': "spongebob@bikinibottom.sea", ], "external_id": "ABC-123", "team_uuid": "d37b1e30-23a2-40b6-ad4c-fa30ab0e9aa7", "shift_type_uuid": "1d1bf619-b097-4461-96a2-f8e7a5249519", "status": "approved", "break_in_minutes": 30, "net_duration_in_minutes": 480, "started_at": "2024-12-01T09:00:00+00:00", "ended_at": "2024-12-01T17:30:00+00:00", "created_at": "2024-12-08T09:00:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `80530` | Shift not found. | ## Get Shift Retrieve a Shift using its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/shifts/{{uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `uuid` *string, required* — The UUID for this Shift. **Response Example** ```json { "data": { "uuid": "69fa84f5-1270-4479-a9f9-75737d6739bf", "contact": [ 'uuid': "33630ed6-fd17-455a-b1ed-7494c3503ab3", 'email': "spongebob@bikinibottom.sea", ], "external_id": "ABC-123", "team_uuid": "d37b1e30-23a2-40b6-ad4c-fa30ab0e9aa7", "shift_type_uuid": "1d1bf619-b097-4461-96a2-f8e7a5249519", "status": "approved", "break_in_minutes": 30, "net_duration_in_minutes": 480, "started_at": "2024-12-01T09:00:00+00:00", "ended_at": "2024-12-01T17:30:00+00:00", "created_at": "2024-12-08T09:00:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `80530` | Shift not found. | ## Update Shift Updates the attributes of a Shift. Shop and contact cannot be updated. ```http PUT https://api.leat.com/api/v3/oauth/clients/shifts/{{uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `uuid` *string, required* — The UUID for this Shift. **Body** - `contact_uuid` *string, optional* — The UUID of the Contact. - `team_uuid` *string, optional* — The UUID of the Team. - `started_at` *string, required* — The start date and time of the Shift in ISO 8601 format. - `ended_at` *string, required* — The end date and time of the Shift in ISO 8601 format. - `status` *string, required* — The Status for this Shift. - `break_in_minutes` *integer, optional* — The break in a minutes for this Shift. **Response Example** ```json { "data": { "uuid": "69fa84f5-1270-4479-a9f9-75737d6739bf", "contact": [ 'uuid': "33630ed6-fd17-455a-b1ed-7494c3503ab3", 'email': "spongebob@bikinibottom.sea", ], "external_id": "ABC-123", "team_uuid": "d37b1e30-23a2-40b6-ad4c-fa30ab0e9aa7", "shift_type_uuid": "1d1bf619-b097-4461-96a2-f8e7a5249519", "status": "approved", "break_in_minutes": 30, "net_duration_in_minutes": 480, "started_at": "2024-12-01T09:00:00+00:00", "ended_at": "2024-12-01T17:30:00+00:00", "created_at": "2024-12-08T09:00:00+00:00", }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `80530` | Shift not found. | | `80531` | Shift status not found. | | `1003` | Invalid input. | ## Delete Shift Deletes an existing Shift. ```http DELETE https://api.leat.com/api/v3/oauth/clients/shifts/{{shift_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `shift_uuid` *string, required* — The Shift's UUID **Possible errors** | Code | Message | | --- | --- | | `80530` | Shift not found. | ## Related - [Teams](https://docs.leat.com/v3/oauth/shift-teams.md) — Create or Update Teams for Shifts - [Shift Types](https://docs.leat.com/v3/oauth/shift-types.md) — Create or Update Shift Types Source: https://docs.leat.com/v3/oauth/shifts --- # Shops > **Warning** > > **Note:** We are in the process of renaming **Shops** to **Business Profiles** Shops play a central role in most transactions within our system. They can represent physical storefronts or digital web shops. The API calls below enable you to retrieve Shop data by listing Shops or retrieving a single Shop. Most transactions and activities take place at a certain Shop, and so it's often a required parameter in POST endpoints. ## List Shops Retrieve all Shops belonging to an Account. In case this call is used for a Loyalty flow, then please check for a Loyalty program. None of the Loyalty API calls where a Shop ID is required will work if the Shop used is not linked to a Loyalty Program. ```http GET https://api.leat.com/api/v3/oauth/clients/shops ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "uuid": "123123", "name": "Krusty Krab", "type": "physical", "reference": null }, { "uuid": "12312312312312", "name": "Chum Bucket Online", "type": "web", "reference": "" }, { "uuid": "2222e1242t3324535", "name": "Conch Street", "type": "physical", "reference": "" }, ], "meta": {} } ``` ## Get Shop Retrieve information of your Shop. This call is useful for getting insights into individual shop settings and attributes. ```http GET https://api.leat.com/api/v3/oauth/clients/shops/{{shop_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `shop_uuid` *string, required* — The UUID of the Shop you want to retrieve. **Response Example** ```json { "data": { "uuid": "123123", "name": "Krusty Krab", "type": "physical", "reference": null }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1008` | Shop not found. | ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Get information and create contacts using our public API - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards Source: https://docs.leat.com/v3/oauth/shops --- # Subscription Types Subscription Types are ways of setting up different categories for your communication. It's like a mailing list. By default, an Account has two Subscription Types: one for marketing purposes, and another for functional emails. ## Enrollment strategies For each Subscription Type, an enrollment `strategy`needs to be chosen, which decides how Contacts can opt in or out of that specific type. There are three options: - **1\. Opt-out:** New Contacts are enrolled automatically and will need to opt out to unsubscribe. - **2\. Opt-in:** New Contacts are not enrolled automatically and will need to opt in to subscribe. - **3\. Double opt-in:** New Contacts are not enrolled automatically and will need to opt in, and then confirm via email to subscribe. For API purposes, when using the opt-in and opt-out strategies, Contacts can be subscribed and unsubscribed via API calls. When using the double opt-in `strategy`, Contacts can be unsubscribed via API, but they cannot be (fully) subscribed. When subscribing a Contact to a double opt-in Subscription Type via API, they will be sent an email asking for confirmation. The email that is sent can be customized in the Business Dashboard. ## List Subscription Types Retrieves a list of all Subscription Types linked to an Account. Useful for getting an overview of the various communication categories and their enrollment strategies. ```http GET https://api.leat.com/api/v3/oauth/clients/subscription-types ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "name": "Bikini Bottom Deals", "description": "A monthly email blast with our best offers!", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": null, "id": 3 }, { "name": "Marketing", "description": "Subscribe here for marketing purposes", "active": true, "strategy": "DOUBLE_OPT_IN", "uuid": "123123123", "id": 1 }, { "name": "Krusty Krab News", "description": "Never miss out on any of our newest features", "active": true, "strategy": "OPT_OUT", "uuid": null, "id": 5 }, { "name": "Events", "description": "Stay in the loop for all our special events", "active": true, "strategy": "OPT_IN", "uuid": null, "id": 4 } ], "meta": [] } ``` ## Related - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards - [Reward Receptions](https://docs.leat.com/v3/oauth/reward-receptions.md) — It's what Contacts are saving up for: Reward Receptions Source: https://docs.leat.com/v3/oauth/subscription-types --- # Tiers You can now offer your Contacts an extra bonus by using Tiers. Tiers allow you to automatically assign Contacts a certain status. For example, consider Bronze, Silver and Gold as Tiers, where a higher total credits received or number of transactions leads to a higher Tier. You can then apply alternative loyalty rules that apply to a specific Tiers. ## List Tiers This API call fetches a list of all Tiers available in your Loyalty Program. It's a handy way to view the different status levels that Contacts can achieve. ```http GET https://api.leat.com/api/v3/oauth/clients/tiers ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "name": "Krusty Krew", "description": "Krusty Krew Members only", "position": 1, "media": null, "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839" } ], "meta": [] } ``` ## Get Tier Use this endpoint to retrieve the current loyalty Tier of a specific Contact. It’s useful for understanding a Contact's loyalty status and offering them tailored Rewards based on their Tier. ```http GET https://api.leat.com/api/v3/oauth/clients/contacts/{{contact_uuid}}/tier ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `contact_uuid` *string, required* — The Contact's UUID from whom you want to retrieve the Tier. **Response Example** ```json { "data": { "name": "Krusty Krew", "description": "Krusty Krew Members only", "position": 1, "media": null, "uuid": "ea77edd4-5a5e-4a6e-aeda-55038c43c839" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55031` | Contact not found. | ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Credit Receptions](https://docs.leat.com/v3/oauth/credit-receptions.md) — The issuing of credits is done using so-called Credit Receptions Source: https://docs.leat.com/v3/oauth/tiers --- # Units Units can be seen as the input values for Credit Receptions. An input value goes in, a certain amount of credits comes out. For most Piggy Clients, 'purchase\_amount' will be the default value, but it can be virtually anything (calories, kilometers, visits; anything countable). An Account always has at least one Unit, and one of the Units is the Account's default (fallback) Unit. This default Unit is used whenever the Unit to be used is not specified. ## List Units Lists all Units for an account. These can subsequently be used to create Credit Receptions, where the unit\_name parameter needs to be supplied if the default unit purchase\_amount is not wished to be used. ```http GET https://api.leat.com/api/v3/oauth/clients/units ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": [ { "name": "purchase_amount", "label": "Purchase Amount", "prefix": "", "is_default": false }, { "name": "test", "label": "Calories", "prefix": "", "is_default": true } ], "meta": {} } ``` ## Create Unit Create a new Unit for your Account with this API call. By default, a Unit always exists for an Account. If `is_default` is set to `true`, the newly created Unit will be the default Unit for the Account, meaning this will be used for Credit Receptions if Unit is not specified. For integrating purposes, however, we recommend the Unit to be explicitly set if a certain Unit should be used (while creating a Credit Reception for example), and not rely on it being the default on creation, as it can later be changed. ```http POST https://api.leat.com/api/v3/oauth/clients/units ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — Internal and unique value for the Unit. Cannot be altered after creation. - `label` *string, required* — Label of the Unit, to be outputted to end users. - `is_default` *boolean, optional* — Specifies whether this Unit is to be the Account's default. Accepts: true, false, 1, 0, yes, no. Defaults to false if not set. - `prefix` *string, optional* — The prefix of the Unit, this will be outputted to end users. Defaults to the first character of the name, if not set. **Response Example** ```json { "data": { "loyalty_program_id": 3545, "name": "test2", "label": "Calories", "is_default": true, "prefix": "t", "format": "int", "updated_at": "2023-11-06T14:39:24.000000Z", "created_at": "2023-11-06T14:39:24.000000Z", "id": 298 }, "meta": {} } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `55045` | Unit already exists. | ## Related - [Contacts](https://docs.leat.com/v3/oauth/contacts.md) — Contacts are the basis of the loyalty system, for whom any action is created - [Rewards](https://docs.leat.com/v3/oauth/rewards.md) — List your rewards and update rewards Source: https://docs.leat.com/v3/oauth/units --- # Visits Visits are replicas of the visits registered in your own system. Leat holds enough of one to tie it to a Contact and a Shop, with the time it started and ended; your system stays the record of what actually happened. > **Warning** > > **Please note:** the *Visits* API is now in beta. Some functionalities may not yet be available at that time. Please note that the API may be subject to change. ## List Visit ```http GET https://api.leat.com/api/v3/oauth/clients/visits ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `limit` *number, optional* — Number of Visits to retrieve (min: 0, max: 500, default: 30) - `page` *number, optional* — Number of page to retrieve (default: 1) - `contact_uuid` *string, optional* — A contact's UUID to filter the Visits by **Response Example** ```json { "data": [ { "uuid": "87824d97-a0a0-4764-9cd2-9df88579fd47", "contact": { "uuid": "6e8ec5a8-0eb7-4e5f-ad20-c0d271d97d92", "email": "spongebob@bikinibottom.sea" }, "external_id": "1a10fef3-cbee-47cb-af4c-8bfbd09d88af", "source": "Website", "starts_at": "2001-01-23T00:00:00+00:00", "ends_at": "2002-02-24T00:00:00+00:00", "created_at": "2025-04-07T13:47:34+00:00", }, { "uuid": "af0078fb-39d4-4ffe-a9a8-2b86edd6f71e", "contact": { "uuid": "3da1b8ed-161e-4733-909d-fc3e65205be3", "email": "mrkrabs@bikinibottom.sea" }, "external_id": "f1e86b94-eb91-4f20-88ec-67d6cb68f034", "source": "Kiosk", "starts_at": "2024-05-01T20:00:00+00:00", "ends_at": "2024-05-01T22:30:00+00:00", "created_at": "2024-04-25T13:35:09+00:00" }, ], "meta": { "current_page": 1, "last_page": 1, "per_page": 30, "total": 5 } } ``` ## Show Visit ```http GET https://api.leat.com/api/v3/oauth/clients/visits/{{uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Response Example** ```json { "data": { "uuid": "1e32b995-b339-4da8-93a1-f2783b2c80c4", "contact": { "uuid": "6e8ec5a8-0eb7-4e5f-ad20-c0d271d97d92", "email": "spongebob@bikinibottom.sea" }, "external_id": "b100ce2c-6abb-4670-b416-abe41bf680c0", "source": "Kiosk", "starts_at": "2001-01-23T00:00:00+00:00", "ends_at": "2002-02-24T00:00:00+00:00", "created_at": "2025-04-07T13:47:34+00:00", }, "meta": [] } ``` ## Create Visit Create a new Visit in the Leat system and returns the Visit entity. ```http POST https://api.leat.com/api/v3/oauth/clients/visits ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` - `contact_uuid` *string, required* — The UUID of the visiting Contact - `shop_uuid` *string, required* — The UUID of the Shop you want to create the Visit for - `external_id` *string, optional* — A unique, external identifier for the Visit - `source` *string, optional* — Source from where the Visit was created - `starts_at` *string, optional* — The start date and time of the Visit (in ISO 8601 format) - `ends_at` *string, optional* — The end date and time of the Visit (in ISO 8601 format) **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "contact": { "uuid": "15ead936-0d0a-40ed-877b-39f22b34be53", "email": "spongebob@bikinibottom.sea" }, "external_id": "ee33d7e5-6cf8-445a-abd8-81e70bf4c8ad", "source": "Website", "starts_at": "2024-05-01T20:00:00+00:00", "ends_at": "2024-05-01T22:30:00+00:00", "created_at": "2024-04-25T13:35:09+00:00" }, "meta": [] } ``` ## Update Visit Updates the attributes of a Visit. Shop and contact cannot be updated. ```http PUT https://api.leat.com/api/v3/oauth/clients/visits/{{uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` - `external_id` *string, optional* — A unique, external identifier for the Visit - `source` *string, optional* — Source from where the Visit was created - `starts_at` *string, optional* — The start date and time of the Visit (in ISO 8601 format) - `ends_at` *string, optional* — The end date and time of the Visit (in ISO 8601 format) **Response Example** ```json { "data": { "uuid": "1e32b995-b339-4da8-93a1-f2783b2c80c4", "contact": { "uuid": "6e8ec5a8-0eb7-4e5f-ad20-c0d271d97d92", "email": "spongebob@bikinibottom.sea" }, "external_id": "b100ce2c-6abb-4670-b416-abe41bf680c0", "source": "Kiosk", "starts_at": "2001-01-23T00:00:00+00:00", "ends_at": "2002-02-24T00:00:00+00:00", "created_at": "2025-04-07T13:47:34+00:00", }, "meta": [] } ``` ## Related - [Loyalty Tokens](https://docs.leat.com/v3/oauth/loyalty-tokens.md) — Create or Claim a Loyalty Token - [Reward Receptions](https://docs.leat.com/v3/oauth/reward-receptions.md) — It's what Contacts are saving up for: Reward Receptions Source: https://docs.leat.com/v3/oauth/visits --- # Vouchers ## List Vouchers Retrieve a paginated list of all Vouchers for an Account, with the option to filter by Promotion, Contact, Voucher status, and more. ```http GET https://api.leat.com/api/v3/oauth/clients/vouchers ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `promotion_uuid` *string, optional* — UUID of the Promotion for which the Vouchers are to be retrieved. - `contact_uuid` *string, optional* — UUID of the Contact for which the Vouchers are to be retrieved. - `status` *string, optional* — Status of the Vouchers, options: ACTIVE, REDEEMED, EXPIRED, DEACTIVATED, INACTIVE and LOCKED. - `limit` *number, optional* — Number of Vouchers to retrieve (min: 0, max: 500, default: 30). - `page` *number, optional* — Page within list to retrieve. **Response Example** ```json { "data": [ { "uuid": "57d93a44-1c9d-4548-8c0e-e6cd71d6170a", "code": "ABC123", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "Krabby Patty of your choice", "expiration_date": null, "activation_date": "2023-03-02T14:45:52+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "shipping": null, "shipping_region": null, }, "contact": null, "promotion": { "uuid": "123", "name": "Free Krabby Patty", "description": "Krabby Patty of your choice", "voucher_limit": 25012, "limit_per_contact": null, "expiration_duration": null, "media": { "type": "image", "value": "https://example.com/image.jpg" } } }, { "uuid": "96ab0da0-f502-4e6d-9b7d-c5b0f5c18795", "code": "DEF456", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "Free Krabby Patty", "expiration_date": null, "activation_date": "2023-03-02T14:45:52+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "shipping": null, "shipping_region": null, }, "contact": { "uuid": "eac16058-d08e-44f3-8bb3-d42afef192cfs", "email": "bubble1@bikinibottom.sea" }, "promotion": { "uuid": "123", "name": "Free Krabby Patty", "description": "Free Krabby Patty", "voucher_limit": 25012, "limit_per_contact": null, "expiration_duration": null, "media": { "type": "image", "value": "https://example.com/image.jpg" } } }, { "uuid": "1390b1e6-ac11-413e-8d20-143640fab2f4", "code": "GHI789", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "Free Krabby Patty", "expiration_date": null, "activation_date": "2023-03-02T14:45:52+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "shipping": null, "shipping_region": null, }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "promotion": { "uuid": "123", "name": "Free Krabby Patty", "description": "Free Krabby Patty", "voucher_limit": 25012, "limit_per_contact": null, "expiration_duration": null, "media": { "type": "image", "value": "https://example.com/image.jpg" } } } ], "meta": { "current_page": 1, "from": 1, "last_page": 9, "per_page": "3", "to": 3, "total": 10 } } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80002` | Promotion not found. | | `55031` | Contact not found. | ## Create Voucher Creates a new Voucher for a Promotion. Only the `promotion_uuid` is required. A specific code can be given to be used, or else a code will be generated by the system. Similarly, if no `activation_date` and/or `expiration_date` is given, the system will calculate those dates by the validity rules set up for the promotion. It is recommended to send in the expiration and activation date **only** if business logic demands its, and the validity logic in the Piggy system is unable to handle that logic. **Note:** If you do send in activation- or expiration dates, keep in mind they need to be in ISO 8601 format and will be stored in UTC time. If the date-time you're sending in is not already in UTC time, please either parse it to UTC beforehand, or pass along the timezone. ```http POST https://api.leat.com/api/v3/oauth/clients/vouchers ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `promotion_uuid` *string, required* — UUID of the Promotion for which the Voucher is to be created. - `code` *string, optional* — The unique code of the Voucher which will serve as its identifier. If not given, a random code will be automatically generated. - `contact_uuid` *string, optional* — The Contact's UUID to be linked to this Voucher. If not given, the recipient will need to identify themselves to use the Voucher. - `expiration_date` *string, optional* — The Vouchers expiration date in ISO 8601 format. Dates are stored in UTC time. If not given, Promotion validity rules apply. - `activation_date` *string, optional* — The Vouchers activation date in ISO 8601 format. Dates are stored in UTC time. If not given, Promotion validity rules apply. - `total_redemptions_allowed` *integer, optional* — Number of times this specific Voucher can be redeemed. If not given, Promotion type and redemptions per voucher will apply. - `custom_attributes` *array, optional* — Additional Custom Attributes for the created Voucher. **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "code": "EXAMPLE-CODE-03785", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "Free Krabby Patty", "expiration_date": "2023-12-12T10:00:00+00:00", "activation_date": "2023-12-10T10:00:00+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "joker": null, "joker2": null, "jokertje1233": null, "someName2": "meer" }, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea" }, "promotion": { "uuid": "123", "name": "Free Krabby Patty", "description": "Free Krabby Patty", "voucher_limit": 25012, "limit_per_contact": null, "expiration_duration": null, "joker": null, "joker2": null, "jokertje1233": null, "someName2": "meer" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80002` | Promotion not found. | | `80003` | Voucher already exists. | | `80008` | Voucher limit reached. | | `55031` | Contact not found. | ## Create Batch of Vouchers Create multiple Vouchers at once for a specific Promotion. Specify the quantity and, optionally, a Contact UUID, `activation_date` and/or `expiration_date`. The system will generate a batch of Vouchers according to the Promotion's rules. ```http POST https://api.leat.com/api/v3/oauth/clients/vouchers/batch ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `promotion_uuid` *string, required* — UUID of the Promotion for which the Vouchers are to be created. - `quantity` *integer, required* — The number of Vouchers you want to generate (min: 1, max: 500)." - `contact_uuid` *string, optional* — The Contact's UUID to be linked to the batch of Vouchers. - `expiration_date` *string, optional* — The expiration date for the batch of Vouchers in ISO 8601 format. Dates are stored in UTC time. If not given, promotion validity rules apply. - `activation_date` *string, optional* — The activation date for the batch of Vouchers in ISO 8601 format. Dates are stored in UTC time. If not given, promotion validity rules apply. - `custom_attributes` *array, optional* — Additional Custom Attributes for the Vouchers. **Response Example** ```json { "data": { "uuid": "bfada889-6a69-4fcf-aed6-42933ae9084d", "type": "vouchers", "quantity": 30, "status": "PENDING", "created_at": "2023-11-27, 17:59" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80002` | Promotion not found. | | `80008` | Voucher limit reached. | | `55031` | Contact not found. | ## Find Voucher Finds a Voucher by its unique code. This call will help you with identifying individual Vouchers and viewing their specifications like whether the Voucher is redeemed or not or when it expires. ```http GET https://api.leat.com/api/v3/oauth/clients/vouchers/find?code={{code}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `code` *string, required* — The unique code of the Voucher, which serves as its identifier. **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "code": "EXAMPLE-CODE-03785", "status": "REDEEMED", "name": "Free Krabby Patty", "description": "Krabby Patty of your choice", "expiration_date": "2023-12-12T10:00:00+00:00", "activation_date": "2023-12-10T10:00:00+00:00", "redeemed_at": "2023-11-13T08:51:00+00:00", "is_redeemed": true, "redemptions_remaining": 0, "times_redeemed": 1, "total_redemptions_allowed": 1, "attributes": { "plu": "ABC123", }, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea" }, "promotion": { "uuid": "123", "name": "Free Krabby Patty", "description": "Krabby Patty of your choice", "voucher_limit": 25012, "limit_per_contact": null, "expiration_duration": null, "media": { "type": "image", "value": "https://example.com/image.jpg" } } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80004` | Voucher not found. | ## Update Voucher Update the status of a Voucher. This endpoint allows you to activate or deactivate a Voucher, changing its usability status as needed. ```http PUT https://api.leat.com/api/v3/oauth/clients/vouchers/{{voucher_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `voucher_uuid` *string, required* — The UUID of the Voucher that you want to update. **Body** - `status` *string, required* — Can be either ACTIVE or DEACTIVATED. - `custom_attributes` *array, optional* — Additional Custom Attributes as a key-value array. - `expiration_date` *string, optional* — New expiration date for the Voucher. Note: this can only be done for inactive and active Vouchers. **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "code": "EXAMPLE-CODE-03785", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "Krabby Patty of your choice", "expiration_date": "2023-12-12T10:00:00+00:00", "activation_date": "2023-12-10T10:00:00+00:00", "redeemed_at": null, "is_redeemed": false, "redemptions_remaining": 5, "times_redeemed": 0, "total_redemptions_allowed": 5, "attributes": { "plu": "ABC123", }, "contact": null, "promotion": { "uuid": "123", "name": "Free Krabby Patty", "description": "Krabby Patty of your choice", "voucher_limit": 25012, "limit_per_contact": null, "expiration_duration": null, "media": { "type": "image", "value": "https://example.com/image.jpg" } } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80004` | Voucher not found. | ## Check Voucher Validity Validates if the Voucher can be redeemed at this point by the Contact. ```http POST https://api.leat.com/api/v3/oauth/clients/vouchers/{{voucher_uuid}}/validate ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Input parameters** - `voucher_uuid` *string, required* — The UUID of the Voucher to check - `contact_uuid` *string, required if no Contact linked* — The UUID of the Contact who would redeem the Voucher - `shop_uuid` *string, required* — The UUID of the Shop where the Voucher would be redeemed ## Redeem Voucher Redeems a Voucher for a Contact. If the Voucher is already linked to a Contact – because it was sent in an automated email for instance – the call only requires the Voucher's code for redeeming it. If the Voucher isn't yet linked to a Contact – because a series of Vouchers were printed as flyers to be given away for instance – then the `contact_uuid`is required as well as the Voucher code. Please note that when using locked Vouchers, you should send in your `release_key`here as well. ```http POST https://api.leat.com/api/v3/oauth/clients/vouchers/redeem ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `code` *string, required* — The unique code of the Voucher which will serve as its identifier - `shop_uuid` *string, required* — UUID of the Shop through which the voucher is redeemed. - `contact_uuid` *string, required_if* — Not required if Contact is already assigned to Voucher, but could serve as identity check if given nevertheless. In case no Contact is assigned to the Voucher, the contact_uuid must be supplied. - `release_key` *string, optional* — The release key that was returned when locking the voucher (Only applicable if locking is used). **Response Example** ```json { "data": { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "code": "EXAMPLE-CODE-03785", "status": "REDEEMED", "name": "Free Krabby Patty", "description": "Krabby Patty of your choice", "expiration_date": "2023-12-12T10:00:00+00:00", "activation_date": "2023-12-10T10:00:00+00:00", "redeemed_at": "2023-11-13T08:51:00+00:00", "is_redeemed": true, "redemptions_remaining": 0, "times_redeemed": 1, "total_redemptions_allowed": 1, "attributes": { "plu": "ABC12" }, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea" }, "promotion": { "uuid": "123", "name": "Free Krabby Patty", "description": "Choice of Original or Double Deluxe", "voucher_limit": 25012, "limit_per_contact": null, "expiration_duration": null, "plu": "ABC12" }, "voucher_redemption": { "uuid": "8fsl298-oaisdf98-asdf923-afsdoij", "created_at": "2023-11-13T08:51:00+00:00" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80004` | Voucher not found. | | `80006` | Voucher has expired. | | `80005` | Voucher code as already been redeemed. | | `80012` | Voucher is deactivated. | | `80014` | Voucher is inactive. | ## Lock Voucher (Optional) Using the lock functionality, you can make sure that a Voucher cannot be used by another process at the same time as well as return the Voucher to its ACTIVE state when a failure occurs on your side (using the Release call). Using the locking method, you can make sure the Voucher only reaches a REDEEMED state when everything completed successful on your side. When locking a Voucher, a release key is returned which should then be used to either redeem the Voucher later on, or to restore the Voucher to its ACTIVE state using the release call. If the lock is not released within an hour, the lock will be released by the system automatically and the Voucher will be restored to its ACTIVE state. ```http POST https://api.leat.com/api/v3/oauth/clients/vouchers/{{voucher_uuid}}/lock ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `voucher_uuid` *string, required* — The UUID of the Voucher that you want to lock. **Response Example** ```json { "data": { "voucher": { "uuid": "11fe9574-800a-4832-ae64-e90d0192ca9a", "status": "LOCKED" }, "lock": { "release_key": "5cc7e94a-7aa2-4f4a-8a8c-c20ab3a086ce", "locked_at": "2023-11-13T09:01:46+00:00", "unlocked_at": null, "system_release_at": "2023-11-13T10:01:46+00:00" } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80004` | Voucher not found. | | `80006` | Voucher has expired. | | `80005` | Voucher code as already been redeemed. | | `80012` | Voucher is deactivated. | | `80017` | Voucher is already locked. | ## Release Voucher (Optional) When using Voucher locking, there is also the process of releasing the Voucher. Using the call you can release the Voucher from its lock using the release key that was retrieved during the initial lock. **Extra information:** When redeeming a Voucher that was previously locked, the system will release the lock automatically (you need to provide the release key as well). However, if your system wants to release the Voucher manually you can do so using this call. If the release is not done within the hour, the system will release it automatically. ```http POST https://api.leat.com/api/v3/oauth/clients/vouchers/{{voucher_uuid}}/release ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `voucher_uuid` *string, required* — The UUID of the Voucher that you want to release. **Body** - `release_key` *string, required* — The release key of the Voucher that was given upon locking it. **Response Example** ```json { "data": { "voucher": { "uuid": "11fe9574-800a-4832-ae64-e90d0192ca9a", "status": "ACTIVE" }, "lock": { "release_key": "5cc7e94a-7aa2-4f4a-8a8c-c20ab3a086ce", "locked_at": "2023-11-13T09:01:46+00:00", "unlocked_at": "2023-11-13T09:03:32+00:00", "system_release_at": null } }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80004` | Voucher not found. | | `80006` | Voucher has expired. | | `80005` | Voucher code as already been redeemed. | ## Reverse Voucher Redemption Reverses a Voucher Redemption. ```http POST https://api.leat.com/api/v3/oauth/clients/vouchers/{{uuid}}/reverse ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `uuid` *string, required* — The UUID of the Voucher Redemption **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80004` | Voucher not found. | ## Multi Redeem Voucher Redeems a Voucher multiple times at once for a Contact. If the Voucher isn't yet linked to a Contact – because a series of Vouchers were printed as flyers to be given away for instance – then the `contact_uuid`is required as well as the Voucher code. Please note that when using locked Vouchers, you should send in your `release_key`here as well. ```http POST https://api.leat.com/api/v3/oauth/clients/vouchers/multi-redeem ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `code` *string, required* — The unique code of the Voucher which will serve as its identifier - `number_of_times` *integer, required* — Number of times the Voucher is to be redeemed (min: 1, max: 50). Note: Only multi-use Vouchers can be used here. - `contact_uuid` *string, required_if* — Not required if Contact is already assigned to Voucher, but could serve as identity check if given nevertheless. In case no Contact is assigned to the Voucher, the contact_uuid must be supplied. - `shop_uuid` *string, required* — The Shop or Business Profile where the Voucher is redeemed - `release_key` *string, optional* — The release key that was returned when locking the voucher (Only applicable if locking is used). **Response Example** ```json { "data": { "voucher: { "uuid": "62f6dc8c-4ff1-4c42-99ba-713c68ed10a0", "code": "EXAMPLE-CODE-03785", "status": "REDEEMED", "name": "Free Krabby Patty", "description": "Choice of Original or Double Deluxe", "expiration_date": "2023-12-12T10:00:00+00:00", "activation_date": "2023-12-10T10:00:00+00:00", "redeemed_at": "2023-11-13T08:51:00+00:00", "is_redeemed": true, "attributes": { "plu": "ABC12" }, "contact": { "uuid": "123", "email": "spongebob@bikinibottom.sea" }, "promotion": { "uuid": "123", "name": "Free Krabby Patty", "description": "Choice of Original or Double Deluxe", "voucher_limit": 1000, "limit_per_contact": null, "expiration_duration": null, "plu": "ABC12" } }, "voucher_redemptions": [ { "uuid": "abc123-890asd-d8a6s7e", "created_at": "2023-11-13T08:51:00+00:00" }, { "uuid": "789bacs-asd1234-d8a6s7e", "created_at": "2023-11-13T08:51:00+00:00" }, { "uuid": "fsa12aa-g332as-xv234a", "created_at": "2023-11-13T08:51:00+00:00" } ] }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80004` | Voucher not found. | | `80006` | Voucher has expired. | | `80005` | Voucher code as already been redeemed. | | `80012` | Voucher is deactivated. | | `80014` | Voucher is inactive. | | `80019` | Voucher doesn't have enough redemptions left. | | `80020` | Promotion is inactive. | ## Related - [Vouchers](https://docs.leat.com/v3/oauth/promotions.md) — Manage the Promotions for which the Vouchers are created - [Promotion Attributes](https://docs.leat.com/v3/oauth/promotion-attributes.md) — Customize your Promotions and fully integrate with your software Source: https://docs.leat.com/v3/oauth/vouchers --- # Webhooks For a number of events, you can subscribe to Webhooks to receive event data. Most information concerning the event itself is on the Webhook data. For most related entities, you may only receive its UUID, which then requires an API call to retrieve more data if needed. Webhooks can be subscribed to using the endpoints on this page, the Business Dashboard, or Automations. In case of the OAuth API and the Business Dashboard, webhook subscriptions are set up for events and will be fired for each and every one of those events. Within the Automations tool, you can set up a more delicate webhook system, only firing after some set of filters have passed. This way, you can prevent an overload of irrelevant API calls being sent to your servers. ## Webhook versions A subscription has a version, and the version decides the shape of the body we post to your endpoint. The event payloads themselves are identical across versions — only the envelope around them differs. > **Note** > > **V2 is the version to build on.** It is becoming the standard for every Leat webhook, and we advise starting there for anything new and porting existing receivers when you get the chance. V1 stays supported for a good while yet; when we do set a retirement date we will announce it well in advance and nothing will change on a live subscription before then. [Webhooks V2](https://docs.leat.com/v3/oauth/webhooks-v2.md)Recommended A batch: an array of events under one envelope. Up to 100 events per request. Deduplicate on `events[].event_uuid`. [Webhooks V1](https://docs.leat.com/v3/oauth/webhooks-v1.md)Supported A single event. One event per request. Deduplicate on `uuid`. Pick the version with the `version` field on [Create Webhook Subscription](https://docs.leat.com/v3/oauth/webhooks.md#create-webhook-subscription). Leaving it out gives you `V1`, so an integration written before V2 existed keeps receiving exactly what it receives today. Every subscription response carries its `version`, so you can always see which shape an endpoint is being sent. ## Available webhook events Open an event to see a complete example of its `data` — the part of the body shaped by the event type, and identical in both webhook versions. The envelope wrapped around it is the subscription's version, not the event's: see [Webhooks V2](https://docs.leat.com/v3/oauth/webhooks-v2.md) or [Webhooks V1](https://docs.leat.com/v3/oauth/webhooks-v1.md) for that. If the event you need is missing, you can request the addition of said event with our support team. ### `contact_created` New contact has been created. ```json { "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea", "custom_attributes": { "favourite_patty": "Krabby Patty Deluxe" }, "credit_balance": { "balance": 0, "previous_balance": 0, "first_transaction_date": null, "last_transaction_date": null, "total_points_received": 0, "total_purchase_amount": 0, "number_of_transactions": 0 }, "locale": "en", "current_display_values": { "firstname": "SpongeBob", "lastname": "SquarePants", "email": "spongebob@bikinibottom.sea" }, "current_values": { "firstname": "SpongeBob", "lastname": "SquarePants", "email": "spongebob@bikinibottom.sea" }, "tier": null, "prepaid_balance": { "balance_in_cents": 0, "balance": 0, "formatted_balance": "€0.00" }, "created_at": "2026-02-18T09:12:44+00:00", "updated_at": "2026-02-18T09:12:44+00:00" } } ``` ### `contact_updated` Contact has been updated. The payload is the whole contact as it is after the change; it doesn't say which attributes changed. ```json { "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea", "custom_attributes": { "favourite_patty": "Krabby Patty Deluxe" }, "credit_balance": { "balance": 340, "previous_balance": 250, "first_transaction_date": "2026-01-04T11:02:10+00:00", "last_transaction_date": "2026-02-18T09:12:44+00:00", "total_points_received": 340, "total_purchase_amount": 8900, "number_of_transactions": 7 }, "locale": "en", "current_display_values": { "firstname": "SpongeBob", "lastname": "SquarePants", "email": "spongebob@bikinibottom.sea" }, "current_values": { "firstname": "SpongeBob", "lastname": "SquarePants", "email": "spongebob@bikinibottom.sea" }, "tier": { "uuid": "ce1eb0a3-9b26-4f2f-9f1b-6f5b8c0a2d41", "name": "Fry Cook", "description": "Spatula in hand, grease on apron.", "position": 2, "media": null }, "prepaid_balance": { "balance_in_cents": 1500, "balance": 15, "formatted_balance": "€15.00" }, "created_at": "2026-01-04T11:02:10+00:00", "updated_at": "2026-02-18T09:12:44+00:00" } } ``` ### `contact_was_subscribed` Contact has been subscribed to a subscription type. ```json { "contact_subscription": { "data": { "source": "register" }, "subscription_type": { "uuid": "b74f2e08-6c15-4a93-8d27-5e0b9c3f1a76", "name": "Krusty Krab newsletter", "description": "Weekly specials, straight from the kitchen.", "strategy": "OPT_IN" }, "contact": "spongebob@bikinibottom.sea" } } ``` ### `contact_was_unsubscribed` Contact has been unsubscribed from a subscription type. ```json { "contact_subscription": { "data": { "source": "email_link" }, "subscription_type": { "uuid": "b74f2e08-6c15-4a93-8d27-5e0b9c3f1a76", "name": "Krusty Krab newsletter", "description": "Weekly specials, straight from the kitchen.", "strategy": "OPT_IN" }, "contact": "spongebob@bikinibottom.sea" } } ``` ### `contact_identifier_created` New contact identifier created for a contact. ```json { "contact_identifier": { "value": "KRAB-0042", "name": "Loyalty card", "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00" }, "active": true, "is_default": true, "created_at": "2026-01-04T11:02:11+00:00", "updated_at": "2026-01-04T11:02:11+00:00" } } ``` ### `contact_identifier_updated` Contact identifier has been updated. ```json { "contact_identifier": { "value": "7d3b5f91-4a28-4c60-b19e-2f8c6d4a7e35", "name": "Loyalty card", "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00" }, "active": false, "is_default": true, "created_at": "2026-02-18T09:30:00+00:00", "updated_at": "2026-02-23T14:39:52+00:00" } } ``` ### `tier_reached` Contact has moved up to a new tier. `from_tier` is `null` when the contact had no tier before. ```json { "contact_tier_change": { "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "from_tier": { "uuid": "4e7a2c85-1b36-4d90-a5f2-8c1e6b3d704", "name": "Deckhand" }, "to_tier": { "uuid": "ce1eb0a3-9b26-4f2f-9f1b-6f5b8c0a2d41", "name": "Fry Cook" }, "created_at": "2026-02-23T14:39:52+00:00" } } ``` ### `tier_dropped` Contact has moved down to a lower tier. `to_tier` is `null` when the contact no longer has a tier. ```json { "contact_tier_change": { "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "from_tier": { "uuid": "ce1eb0a3-9b26-4f2f-9f1b-6f5b8c0a2d41", "name": "Fry Cook" }, "to_tier": { "uuid": "4e7a2c85-1b36-4d90-a5f2-8c1e6b3d704", "name": "Deckhand" }, "created_at": "2026-02-23T14:39:52+00:00" } } ``` ### `credit_reception_created` New credit reception has been created for a contact. ```json { "credit_reception": { "uuid": "7c2d9e15-4a83-4b60-9c17-3d5e8f1a2b46", "type": 1, "credits": 90, "unit_value": "18.50", "unit": { "name": "EUR", "label": "Euro" }, "contact_identifier": { "name": "Loyalty card", "value": "KRAB-0042" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea", "loyalty_balance": { "balance": 340, "previous_balance": 250, "last_transaction_date": "2026-02-18T09:12:44+00:00", "last_transaction_credits": 90, "total_credits_received": 340, "total_purchase_amount": 8900, "number_of_transactions": 7 } }, "channel": 10, "created_at": "2026-02-18T09:12:44+00:00", "custom_attributes": { "receipt_number": "KK-2026-0184" } } } ``` ### `points_transaction_created` Points have been credited to or debited from a contact. ```json { "points_transaction": { "uuid": "5c9f3a17-8e42-4b05-9d61-3a7c2f8e5b90", "points": 120, "type": "DirectPointsTransaction", "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" } } } ``` ### `prepaid_transaction_created` New prepaid transaction has been created for a contact. ```json { "prepaid_transaction": { "uuid": "4d7e1f28-9a06-4c53-b8d1-2e5f9a7c3b10", "amount_in_cents": -750, "amount": -7.5, "formatted_amount": "-€7.50", "currency": "EUR", "created_at": "2026-02-18T12:41:09+00:00", "channel": "OAUTH_API", "contact_identifier": { "name": "Loyalty card", "value": "KRAB-0042" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" } } } ``` ### `giftcard_created` New giftcard has been created. ```json { "giftcard": { "uuid": "2b6e9d40-7c13-4f85-a927-6d1b3e8c5a72", "type": "DIGITAL", "hash": "KRAB-4821-9307-5516", "amount_in_cents": 5000, "upgradeable": false, "active": true, "expiration_date": "2027-02-18T23:59:59+00:00", "amount": 50 } } ``` ### `giftcard_updated` Giftcard has been updated, including when its balance changes. ```json { "giftcard": { "uuid": "2b6e9d40-7c13-4f85-a927-6d1b3e8c5a72", "type": "DIGITAL", "hash": "KRAB-4821-9307-5516", "amount_in_cents": 3750, "upgradeable": false, "active": true, "expiration_date": "2027-02-18T23:59:59+00:00", "amount": 37.5 } } ``` ### `giftcard_transaction_created` New giftcard transaction has been created. ```json { "giftcard_transaction": { "uuid": "6b3f8d21-7c04-4a95-8e17-5d2b9f1c3a68", "giftcard": { "uuid": "2e9c7b45-1a38-4d06-9f52-7b4e8c1d5a93", "hash": "KRUSTY-GIFT-8842" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28" }, "giftcard_id": 5182, "business_profile_id": 31, "amount_in_cents": -1250, "settled": true, "created_at": "2026-02-18T13:05:27+00:00" } } ``` ### `voucher_created` New voucher has been created. ```json { "voucher": { "uuid": "3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "code": "PATTY-2026", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "One free Krabby Patty, on the house.", "expiration_date": "2026-03-31T23:59:59+00:00", "activation_date": "2026-02-18T00:00:00+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "campaign": "Winter blast" }, "total_redemptions_allowed": 1, "times_redeemed": 0, "redemptions_remaining": 1, "add_to_wallet_url": "https://krustykrab.leat.com/add-to-wallet/vouchers?uuid=3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "promotion": { "uuid": "8c1e4a72-5d39-4b08-9e26-1f7c3b5d8a04", "name": "Krabby Patty giveaway", "description": "A free patty for every loyal customer.", "media": null, "type": "SINGLE_USE" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "created_at": "2026-02-18T09:30:00+00:00", "updated_at": "2026-02-18T09:30:00+00:00" } } ``` ### `voucher_updated` Voucher has been updated. ```json { "voucher": { "uuid": "3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "code": "PATTY-2026", "status": "INACTIVE", "name": "Free Krabby Patty", "description": "One free Krabby Patty, on the house.", "expiration_date": "2026-04-30T23:59:59+00:00", "activation_date": "2026-02-18T00:00:00+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "campaign": "Winter blast" }, "total_redemptions_allowed": 1, "times_redeemed": 0, "redemptions_remaining": 1, "add_to_wallet_url": "https://krustykrab.leat.com/add-to-wallet/vouchers?uuid=3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "promotion": { "uuid": "8c1e4a72-5d39-4b08-9e26-1f7c3b5d8a04", "name": "Krabby Patty giveaway", "description": "A free patty for every loyal customer.", "media": null, "type": "SINGLE_USE" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "created_at": "2026-02-18T09:30:00+00:00", "updated_at": "2026-02-19T08:14:52+00:00" } } ``` ### `voucher_expired` Voucher has passed its expiration date. ```json { "voucher": { "uuid": "3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "code": "PATTY-2026", "status": "EXPIRED", "name": "Free Krabby Patty", "description": "One free Krabby Patty, on the house.", "expiration_date": "2026-03-31T23:59:59+00:00", "activation_date": "2026-02-18T00:00:00+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "campaign": "Winter blast" }, "total_redemptions_allowed": 1, "times_redeemed": 0, "redemptions_remaining": 1, "add_to_wallet_url": "https://krustykrab.leat.com/add-to-wallet/vouchers?uuid=3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "promotion": { "uuid": "8c1e4a72-5d39-4b08-9e26-1f7c3b5d8a04", "name": "Krabby Patty giveaway", "description": "A free patty for every loyal customer.", "media": null, "type": "SINGLE_USE" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "created_at": "2026-02-18T09:30:00+00:00", "updated_at": "2026-04-01T00:00:11+00:00" } } ``` ### `voucher_redeemed` Contact has redeemed a voucher. ```json { "voucher": { "uuid": "3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "code": "PATTY-2026", "status": "REDEEMED", "total_redemptions_allowed": 1, "times_redeemed": 1, "redemptions_remaining": 0, "expiration_date": "2026-03-31T23:59:59+00:00", "redeemed_at": "2026-02-20T18:22:31+00:00", "created_at": "2026-02-18T09:30:00+00:00", "updated_at": "2026-02-20T18:22:31+00:00", "custom_attributes": [ { "campaign": "Winter blast" } ], "promotion": { "uuid": "8c1e4a72-5d39-4b08-9e26-1f7c3b5d8a04", "name": "Krabby Patty giveaway", "description": "A free patty for every loyal customer.", "type": "SINGLE_USE" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" } } } ``` ### `reward_updated` Reward has been updated. ```json { "reward": { "uuid": "c0b7a458-3d21-4f96-8a54-2e9b6d1c7f03", "title": "Free Kelp Shake", "description": "A shake made from the freshest kelp in the sea.", "cost_price": 150, "required_credits": 200, "is_active": true, "reward_type": "PHYSICAL", "stock": 48, "attributes": { "category": "Drinks" }, "created_at": "2026-01-09T08:25:41+00:00", "updated_at": "2026-02-23T11:16:07+00:00" } } ``` ### `reward_redeemed` Contact has redeemed a reward. `previous_balance` is the balance before the points were deducted. ```json { "reward_redemption": { "uuid": "8f2c7b51-3d94-4e06-b8a3-1c5e9f2d6a48", "points": 250, "reward": { "title": "Free Krabby Patty", "uuid": "6a3e8d29-4f17-4b52-9c80-7e2b5a1d3f64" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea", "loyalty_balance": { "balance": 430, "previous_balance": 180, "last_transaction_date": "2026-02-23T14:39:52+00:00", "last_transaction_credits": 250, "total_credits_received": 1680, "total_purchase_amount": 24500, "number_of_transactions": 23 } }, "channel": "REGISTER", "created_at": "2026-02-23T14:39:52+00:00" } } ``` ### `promotion_updated` Promotion has been updated. ```json { "promotion": { "uuid": "8c1e4a72-5d39-4b08-9e26-1f7c3b5d8a04", "name": "Krabby Patty giveaway", "description": "A free patty for every loyal customer.", "voucher_limit": 500, "limit_per_contact": 1, "expiration_duration": 30, "attributes": { "campaign": "Winter blast" }, "created_at": "2026-02-01T09:00:00+00:00", "updated_at": "2026-02-23T14:39:52+00:00" } } ``` ### `order_created` New order has been created. `result` describes what the order earned the contact, and is `null` when the order ran no loyalty action. ```json { "order": { "uuid": "7a1c4e92-6b38-4d75-9f02-5c8e3a7b1d46", "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" }, "currency": "EUR", "external_identifier": "ORD-2026-0412", "reference": "Table 7", "status": "COMPLETED", "payment_status": "PAID", "formatted_total_order_amount": "€24.50", "order_amount": 2700, "total_charges_amount": 250, "total_discount_amount": 500, "total_order_amount": 2450, "paid_at": "2026-03-02T19:20:04+00:00", "completed_at": "2026-03-02T19:22:38+00:00", "line_items": [ { "uuid": "c5f28a01-9d74-4b63-8e15-3a7c2b6d9f48", "external_identifier": "SKU-PATTY", "name": "Krabby Patty", "quantity": 2, "price": 950, "total_amount": 1900, "discount_amount": 500, "product": { "external_identifier": "PROD-PATTY", "name": "Krabby Patty", "categories": [ { "external_identifier": "CAT-BURGERS", "name": "Burgers" } ] }, "sub_line_items": [ { "uuid": "e91b6d35-4c82-4a07-b834-5f1a9c6e2d70", "external_identifier": "SKU-EXTRA-CHEESE", "name": "Extra cheese", "quantity": 1, "price": 100, "total_amount": 100, "discount_amount": 0, "product": null } ] }, { "uuid": "1d7e4b90-8a25-4f36-9c01-6b3d5e8a2f74", "external_identifier": "SKU-KELP-SHAKE", "name": "Kelp shake", "quantity": 1, "price": 800, "total_amount": 800, "discount_amount": 0, "product": { "external_identifier": "PROD-KELP-SHAKE", "name": "Kelp shake", "categories": [] }, "sub_line_items": [] } ], "applied_discounts": [ { "uuid": "4b2a8f16-7d53-4e91-a6c0-2f8b5d1e3a97", "external_identifier": "DISC-LOYAL", "name": "Loyalty discount", "amount": 500, "type": "PERCENTAGE", "value": 25, "applied_to": "LINE_ITEM", "line_items": [ { "external_identifier": "SKU-PATTY" } ], "sub_line_items": [], "charges": [] } ], "charges": [ { "uuid": "9c3f7a24-5e18-4b60-8d92-1a6c4b7e5f03", "external_identifier": "CHG-SERVICE", "type": "SERVICE_FEE", "name": "Service fee", "order_charge_definition": { "external_identifier": "CHGDEF-SERVICE", "name": "Service fee" }, "amount": 250, "discount_amount": 0, "total_amount": 250 } ], "offers": [ { "type": "VOUCHER", "uuid": "3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "name": "Free Krabby Patty" } ], "payments": [ { "type": "GIFTCARD", "uuid": "2b6e9d40-7c13-4f85-a927-6d1b3e8c5a72", "amount_in_cents": 2450 } ], "created_at": "2026-03-02T19:05:12+00:00", "updated_at": "2026-03-02T19:22:38+00:00" }, "result": { "type": "CREDIT_RECEPTION", "points": 245, "new_points_balance": 675 } } ``` ### `booking_created` New booking has been created. ```json { "booking": { "uuid": "0d4a7e63-9c21-4f38-b5e7-2a8c6d1b495", "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" }, "external_id": "RES-40219", "source": "formitable", "starts_at": "2026-03-02T18:00:00+00:00", "ends_at": "2026-03-02T20:00:00+00:00", "checked_in_at": null, "company_name": "Bikini Bottom Jellyfishing Club", "number_of_people": 4, "status": "confirmed", "prepaid_amount": 0, "created_at": "2026-02-23T14:39:52+00:00" } } ``` ### `booking_updated` Booking has been updated, including when a guest checks in. ```json { "booking": { "uuid": "0d4a7e63-9c21-4f38-b5e7-2a8c6d1b495", "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" }, "external_id": "RES-40219", "source": "formitable", "starts_at": "2026-03-02T18:00:00+00:00", "ends_at": "2026-03-02T20:00:00+00:00", "checked_in_at": "2026-03-02T18:04:11+00:00", "company_name": "Bikini Bottom Jellyfishing Club", "number_of_people": 6, "status": "checked_in", "prepaid_amount": 0, "created_at": "2026-02-23T14:39:52+00:00" } } ``` ### `visit_created` Contact has visited a business profile. ```json { "visit": { "contact": { "email": "spongebob@bikinibottom.sea" }, "business_profile": { "uuid": "1f9a4c73-5d2b-4e81-b6f0-9c3a7d5e1b28", "name": "Krusty Krab" }, "uuid": "3e8b1f47-2a95-4c60-8d13-7b5c9e2a6f80", "external_id": "VISIT-8842", "source": "register", "starts_at": "2026-03-02T18:04:11+00:00", "ends_at": "2026-03-02T19:22:38+00:00", "created_at": "2026-03-02T18:04:11+00:00" } } ``` ### `form_submission_completed` Contact has submitted and completed a form. ```json { "form_submission": { "form": { "uuid": "a4fb2960-ef15-43da-96e0-18aa83861ace", "name": "Krusty Krab feedback" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "values": { "firstname": "SpongeBob", "email": "spongebob@bikinibottom.sea", "how_was_your_patty": "Krusty, just the way I like it" }, "created_at": "2026-02-21T10:04:18+00:00", "updated_at": "2026-02-21T10:04:18+00:00" } } ``` ### `referral_completed` Contact has completed a referral. ```json { "referral": { "uuid": "5f8b2c47-1d93-4a06-9e52-7b3d8c1a4f69", "status": "COMPLETED" }, "referring_contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea", "credit_balance": { "balance": 440 }, "current_display_values": { "firstname": "SpongeBob", "lastname": "SquarePants" }, "current_values": { "firstname": "SpongeBob", "lastname": "SquarePants" }, "tier": { "uuid": "ce1eb0a3-9b26-4f2f-9f1b-6f5b8c0a2d41", "name": "Fry Cook", "description": "Spatula in hand, grease on apron.", "media": null }, "prepaid_balance": { "balance_in_cents": 1500 }, "created_at": "2026-01-04T11:02:10+00:00", "updated_at": "2026-02-22T15:48:03+00:00" }, "referred_contact": { "uuid": "e2a7c6b4-8d15-4930-b7e2-5c1f9a3d8b46", "email": "patrick@bikinibottom.sea", "credit_balance": { "balance": 50 }, "current_display_values": { "firstname": "Patrick", "lastname": "Star" }, "current_values": { "firstname": "Patrick", "lastname": "Star" }, "tier": null, "prepaid_balance": { "balance_in_cents": 0 }, "created_at": "2026-02-22T15:47:55+00:00", "updated_at": "2026-02-22T15:48:03+00:00" } } ``` ## Verifying the Webhook's signature On each webhook request, a `Signature` header is set. This header is a hash consisting of the json encoded payload and the secret set on the webhook subscription. If no secret is set on the subscription itself, the Account secret is used instead. The signature is compiled as follows: ```php $payloadJson = json_encode($payload); $signature = hash_hmac('sha256', $payloadJson, $secret); ``` The signature can subsequently be used on each request to check if it hasn't been tampered with. ## Retries If the webhook request doesn't receive a response within the `2xx` range, we try again. A delivery gets **five attempts in total**, with a growing wait between them — roughly 10 seconds, then 100, then 1,000, then 10,000. Retries are picked up once a minute, so each wait is rounded up to the next minute. After the fifth failed attempt the delivery is marked failed and isn't sent again; the subscription itself stays active. How long we wait for your response, and what a retry re-sends, depend on the subscription's version: [V2 delivery and retries](https://docs.leat.com/v3/oauth/webhooks-v2.md#delivery-and-retries) or [V1 delivery and retries](https://docs.leat.com/v3/oauth/webhooks-v1.md#delivery-and-retries-v1). ## List Webhook Subscriptions Lists an Account's current Webhook Subscriptions. ```http GET https://api.leat.com/api/v3/oauth/clients/webhook-subscriptions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `event_type` *string, optional* — Only retrieve Webhook Subscription of a specific event type (options: see available webhook event types). - `status` *string, optional* — Only retrieve Webhook Subscription of a specific status (options: ACTIVE, INACTIVE). **Response Example** ```json { "data": [ { "uuid": "f1dc2d67-caa8-4919-a537-66491a94ed2f", "name": "New Krusty Krew Member", "event_type": "contact_created", "url": "http://api.example.com/piggy/webhooks", "status": "ACTIVE", "version": "V1", "created_at": "2023-10-20T11:46:22+00:00" }, { "uuid": "5062eeac-bbeb-4903-b81e-2422793ff151", "name": "Krabby Patty Special Updated", "event_type": "promotion_updated", "url": "http://api.example.com/piggy/webhooks", "status": "ACTIVE", "version": "V1", "created_at": "2023-10-20T11:48:43+00:00" }, { "uuid": "33ee3999-4230-4e3e-b320-cbcbe719bc45", "name": "Friend Brought to Krusty Krab", "event_type": "referral_completed", "url": "http://api.example.com/piggy/webhooks", "status": "INACTIVE", "version": "V1", "created_at": "2023-10-20T11:49:19+00:00" }, ], "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Create Webhook Subscription Subscribes to a specific Webhook event. Pass `version` to choose the envelope your endpoint receives — see [Webhook versions](https://docs.leat.com/v3/oauth/webhooks.md#webhook-versions). Leave it out and you get V1, so pass `V2` for anything new. An integration has one subscription per event type. When the token belongs to a private app or another integration that already has a subscription for this `event_type`, that subscription is overwritten with the name, url, secret and version you send and set to active, rather than a second one being created. ```http POST https://api.leat.com/api/v3/oauth/clients/webhook-subscriptions ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Body** - `name` *string, required* — A name given to the subscription for internal purposes. - `event_type` *string, required* — The event type to listen to, can only choose from available event types. - `url` *string, required* — The endpoint to which webhook data is to be sent. - `secret` *string, optional* — The secret used to sign the Signature header. Defaults to the account secret. - `version` *string, optional* — The envelope your endpoint is sent: V2 for a batch of events, V1 for a single one. Defaults to V1. V2 is the version we advise building on. **Response Example** ```json { "data": { "uuid": "59165ef0-1afd-4baf-b1f7-ffee287aff32", "name": "New Krusty Krew Member", "event_type": "contact_created", "url": "http://api.example.com/piggy/webhooks", "status": "ACTIVE", "version": "V1", "created_at": "2023-11-13T12:32:43+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | ## Get Webhook Subscription Retrieve a specific Webhook Subscription by specifying its UUID. ```http GET https://api.leat.com/api/v3/oauth/clients/webhook-subscriptions/{{webhook_subscription_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `webhook_subscription_uuid` *string, required* — The UUID of the Webhook Subscription you want to retrieve. **Response Example** ```json { "data": { "uuid": "59165ef0-1afd-4baf-b1f7-ffee287aff32", "name": "Bikini Bottom Alert", "event_type": "contact_created", "url": "http://yourwebsite.com/somethingsomething", "properties": [], "status": "ACTIVE", "version": "V1", "created_at": "2023-11-13T12:32:43+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `80300` | Webhook subscription not found. | ## Update Webhook Subscription Updates an existing Webhook Subscription. The subscription's `event_type`and `secret`cannot be altered after creation. ```http PUT https://api.leat.com/api/v3/oauth/clients/webhook-subscriptions/{{webhook_subscription_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `webhook_subscription_uuid` *string, required* — The UUID of the Webhook Subscription you want to update. **Body** - `name` *string, optional* — A name given to the subscription for internal purposes. - `url` *string, optional* — The endpoint to which webhook data is to be sent. - `status` *string, optional* — The subscription's status (options: ACTIVE, INACTIVE). - `attributes` *array, required | optional* — Required if event_type is contact_updated, even when you only change the status: an array of one or more contact attribute names that exist in the account. It is stored with the subscription but doesn't filter deliveries — every contact update is sent. Ignored for other event types. **Response Example** ```json { "data": { "uuid": "59165ef0-1afd-4baf-b1f7-ffee287aff32", "name": "Updated Bikini Bottom Alert", "event_type": "contact_created", "url": "http://yourwebsite.com/somethingsomething", "properties": [], "status": "ACTIVE", "version": "V1", "created_at": "2023-11-13T12:32:43+00:00" }, "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `1003` | Invalid input. | | `80300` | Webhook subscription not found. | ## Delete Webhook Subscription Removes an existing Webhook Subscription. ```http DELETE https://api.leat.com/api/v3/oauth/clients/webhook-subscriptions/{{webhook_subscription_uuid}} ``` **Headers** - `Authorization: Bearer {{ access_token }}` - `Accept: application/json` **Params** - `webhook_subscription_uuid` *string, required* — The UUID of the Webhook Subscription you want to delete. **Response Example** ```json { "data": [], "meta": [] } ``` **Possible errors** | Code | Message | | --- | --- | | `80300` | Webhook subscription not found. | ## Related - [Webhooks V2](https://docs.leat.com/v3/oauth/webhooks-v2.md) — The batched envelope, and how to subscribe to it - [Tutorial: POS](https://docs.leat.com/v3/tutorials/pos.md) — Learn how to integrate Leat with your Point of Sale system Source: https://docs.leat.com/v3/oauth/webhooks --- # Webhooks V1 > **Note** > > V1 is the original webhook format and remains fully supported — nothing changes on a live V1 subscription. For anything new we advise [V2](https://docs.leat.com/v3/oauth/webhooks-v2.md), which is becoming the standard across Leat. When a retirement date for V1 is set we will announce it well in advance. V1 posts **one event per request**. The event's `data` sits at the top level of the body, under an envelope that identifies the event itself. The events you can subscribe to, and the `data` each one carries, are listed under [Available webhook events](https://docs.leat.com/v3/oauth/webhooks.md#available-webhook-events) — they are identical in both versions. ## The V1 envelope Every V1 request body has the same four keys. **Envelope fields** - `uuid` *string, required* — Identifies this event, and is stable across retries. Deduplicate on it — a retry re-sends the same uuid. - `version` *string, required* — Always V1. A V2 body says V2 here, which is how one endpoint can accept both while you port. - `type` *string, required* — The event type, for example voucher_created. V2 calls this event_type. - `data` *object, required* — The event payload, identical to what V2 sends for the same event type. See Available webhook events for the shape per type. `data` carries a `uuid` of its own for the thing the event is about — the voucher, the contact, the giftcard. That is a different identifier from the envelope's `uuid`: the same voucher produces many events over its life. **Example request body** ```json { "uuid": "d94e2b07-8f31-45c6-9a18-3e7b5d1c2f80", "version": "V1", "type": "voucher_created", "data": { "voucher": { "uuid": "3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "code": "PATTY-2026", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "One free Krabby Patty, on the house.", "expiration_date": "2026-03-31T23:59:59+00:00", "activation_date": "2026-02-18T00:00:00+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "campaign": "Winter blast" }, "total_redemptions_allowed": 1, "times_redeemed": 0, "redemptions_remaining": 1, "add_to_wallet_url": "https://krustykrab.leat.com/add-to-wallet/vouchers?uuid=3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "promotion": { "uuid": "8c1e4a72-5d39-4b08-9e26-1f7c3b5d8a04", "name": "Krabby Patty giveaway", "description": "A free patty for every loyal customer.", "media": null, "type": "SINGLE_USE" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "created_at": "2026-02-18T09:30:00+00:00", "updated_at": "2026-02-18T09:30:00+00:00" } } } ``` ## Delivery and retries One request per event, and one request per subscription: two endpoints subscribed to the same event type each get their own call, and one failing has no effect on the other. We wait up to **3 seconds** for your response. Anything outside the `2xx` range, and any timeout, counts as a failure. A call gets **five attempts in total**, with a growing wait between them — roughly 10 seconds, then 100, then 1,000, then 10,000 — and is marked failed after the fifth. Because a retry re-sends the same `uuid`, deduplicate on it rather than assuming a body arrives once. The `Signature` header is an HMAC-SHA256 of the JSON body using your subscription's secret, or the account secret when the subscription has none — see [Verifying the Webhook's signature](https://docs.leat.com/v3/oauth/webhooks.md#verifying-the-webhooks-signature). It is calculated the same way in both versions. ## Related - [Webhooks V2](https://docs.leat.com/v3/oauth/webhooks-v2.md) — The batched envelope we advise building on instead - [Webhooks](https://docs.leat.com/v3/oauth/webhooks.md) — The event catalogue and the subscription endpoints Source: https://docs.leat.com/v3/oauth/webhooks-v1 --- # Webhooks V2 V2 posts a **batch** of events instead of a single one. Its `events` key is always an array — even when there is exactly one event in it — and that is the whole reason it exists as a separate version: a receiver written against V2 never needs changing when an event type starts arriving in bulk. That matters because some events arrive in floods. A campaign that issues a voucher to 100,000 contacts produces 100,000 events; V1 sends those as 100,000 separate requests to your endpoint, while V2 groups them into roughly 1,000. The event payloads are byte-for-byte the same in both versions, so porting is a change to how you unwrap a request, not to how you read an event. > **Note** > > V2 is becoming the standard for every Leat webhook, and we advise building new integrations on it. Existing [V1](https://docs.leat.com/v3/oauth/webhooks-v1.md) subscriptions keep working — we will announce a retirement date well ahead of time. The events you can subscribe to, and the `data` each one carries, are listed under [Available webhook events](https://docs.leat.com/v3/oauth/webhooks.md#available-webhook-events). This page describes the envelope V2 wraps them in. ## The V2 envelope Every V2 request body has the same five keys. A batch holds events of one type only, so `event_type` describes all of them. **Envelope fields** - `batch_uuid` *string, required* — Identifies this batch. Quote it to support when you need a delivery traced. It is not a deduplication key — a retry of the same batch reuses it. - `version` *string, required* — Always V2. Lets one endpoint accept both versions while you are porting, since a V1 body says V1 here. - `event_type` *string, required* — The event type every event in this batch is of, for example voucher_created. - `count` *number, required* — How many events the events array holds. Between 1 and 100. - `events` *array, required* — The events themselves. Always an array, never a bare object, even when it holds one event. - `events[].event_uuid` *string, required* — Identifies one event, and is stable across retries. This is the key to deduplicate on. - `events[].event_type` *string, required* — Repeats the batch's event type, so an event can be handed off to a handler without the surrounding envelope. - `events[].data` *object, required* — The event payload, identical to what V1 sends for the same event type. See Available webhook events for the shape per type. Note that `data` carries a `uuid` of its own for the thing the event is about — the voucher, the contact, the giftcard. That is a different identifier from `event_uuid`: the same voucher produces many events over its life. **Example request body** ```json { "batch_uuid": "6c679213-2309-480d-8b6a-18c8cc8b90b2", "version": "V2", "event_type": "voucher_created", "count": 2, "events": [ { "event_uuid": "87754a6f-768c-4095-847b-09758e1b4840", "event_type": "voucher_created", "data": { "voucher": { "uuid": "3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "code": "PATTY-2026", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "One free Krabby Patty, on the house.", "expiration_date": "2026-03-31T23:59:59+00:00", "activation_date": "2026-02-18T00:00:00+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "campaign": "Winter blast" }, "total_redemptions_allowed": 1, "times_redeemed": 0, "redemptions_remaining": 1, "add_to_wallet_url": "https://krustykrab.leat.com/add-to-wallet/vouchers?uuid=3a8d5c19-2b74-4e60-9f13-8c5a1d7e4b92", "promotion": { "uuid": "8c1e4a72-5d39-4b08-9e26-1f7c3b5d8a04", "name": "Krabby Patty giveaway", "description": "A free patty for every loyal customer.", "media": null, "type": "SINGLE_USE" }, "contact": { "uuid": "9504d4b2-03c5-41d8-8423-4b1821b6aa00", "email": "spongebob@bikinibottom.sea" }, "created_at": "2026-02-18T09:30:00+00:00", "updated_at": "2026-02-18T09:30:00+00:00" } } }, { "event_uuid": "b1f4c0a8-5d21-4e77-9a3c-2fc02cb324d5", "event_type": "voucher_created", "data": { "voucher": { "uuid": "d5b21c08-4e7f-4a93-8c16-7b0e2a9d5f34", "code": "PATTY-2027", "status": "ACTIVE", "name": "Free Krabby Patty", "description": "One free Krabby Patty, on the house.", "expiration_date": "2026-03-31T23:59:59+00:00", "activation_date": "2026-02-18T00:00:00+00:00", "redeemed_at": null, "is_redeemed": false, "attributes": { "campaign": "Winter blast" }, "total_redemptions_allowed": 1, "times_redeemed": 0, "redemptions_remaining": 1, "add_to_wallet_url": "https://krustykrab.leat.com/add-to-wallet/vouchers?uuid=d5b21c08-4e7f-4a93-8c16-7b0e2a9d5f34", "promotion": { "uuid": "8c1e4a72-5d39-4b08-9e26-1f7c3b5d8a04", "name": "Krabby Patty giveaway", "description": "A free patty for every loyal customer.", "media": null, "type": "SINGLE_USE" }, "contact": { "uuid": "2e70ea71-631f-4b3c-8097-8e0284751c96", "email": "patrick@bikinibottom.sea" }, "created_at": "2026-02-18T09:30:00+00:00", "updated_at": "2026-02-18T09:30:00+00:00" } } } ] } ``` ## How events are batched A batch closes at whichever of two limits it reaches first: **100 events**, or **1MB** of encoded body. The byte limit is what keeps a batch of orders — each carrying line items, charges and discounts — from becoming a multi-megabyte POST, while a batch of vouchers comfortably fits the full hundred. Both limits are ceilings, not targets. Most events happen one at a time, and a single event that nothing else is queued with is sent immediately as a batch of one: `count` of `1` and a one-element `events` array. Nothing is held back waiting for a batch to fill, so a realtime event is no slower on V2 than it is on V1. A single event larger than 1MB on its own still gets its own request rather than being dropped, so treat 1MB as the point at which we stop adding events, not as a cap on what your endpoint can receive. Which event types arrive in bulk is not part of the contract and will grow over time — that is exactly what the always-an-array envelope buys you. Write your handler as a loop over `events` and it will not need touching when an event type starts batching. ## Idempotency > **Warning** > > **Your receiver must be idempotent on `events[].event_uuid`.** This is part of the V2 contract, not a nice-to-have. A batch is retried as a whole. HTTP gives you no way to say "sixty of these hundred landed", so when a batch is retried we re-send every event in it, including the ones your endpoint already processed before it failed. Recording `event_uuid` as you handle each event, and skipping ones you have seen, is what keeps a retry from double-crediting a contact or re-issuing a voucher. `event_uuid` is stable across attempts, and so is the order of the events within a batch. Do **not** deduplicate on `batch_uuid`. It identifies the whole batch, so treating it the way you treat V1's `uuid` would collapse up to a hundred distinct events into one and silently lose the rest. It is there for tracing a delivery with support, nothing else. ## Delivery and retries Each subscription is delivered to independently: two endpoints subscribed to the same event type get the same batch, and one timing out has no effect on the other's delivery or retries. We wait up to **20 seconds** for your response — longer than V1's three, because the work your endpoint does scales with the size of the batch. Anything outside the `2xx` range, and any timeout, counts as a failure. A delivery gets **five attempts in total**, with a growing wait between them — roughly 10 seconds, then 100, then 1,000, then 10,000 — so the last attempt comes a little over 3 hours after the first. A batch that exhausts its attempts is dropped in full, so an endpoint that stays down through the whole schedule loses every event in that batch rather than one. Returning a `2xx` quickly and doing your processing afterwards is the safest shape. Acknowledge the batch, queue the events on your side, then work through them. The `Signature` header is unchanged from V1: an HMAC-SHA256 of the JSON body using your subscription's secret, or the account secret when the subscription has none. It covers the whole batch, not the individual events — verify it against the raw body you received, before you parse it. See [Verifying the Webhook's signature](https://docs.leat.com/v3/oauth/webhooks.md#verifying-the-webhooks-signature). ## Subscribing to V2 Pass `version: "V2"` to [Create Webhook Subscription](https://docs.leat.com/v3/oauth/webhooks.md#create-webhook-subscription). Everything else about the endpoint is the same, and the subscription you get back reports the version it was created with. **Request body** ```json { "name": "Krusty Krab vouchers", "event_type": "voucher_created", "url": "https://api.example.com/leat/webhooks", "version": "V2" } ``` **Response** ```json { "data": { "uuid": "59165ef0-1afd-4baf-b1f7-ffee287aff32", "name": "Krusty Krab vouchers", "event_type": "voucher_created", "url": "https://api.example.com/leat/webhooks", "status": "ACTIVE", "version": "V2", "created_at": "2026-02-18T09:30:00+00:00" }, "meta": [] } ``` Omitting `version` creates a `V1` subscription. That default will not change under you: an integration written before V2 existed keeps getting single-event bodies until it asks for otherwise. ### Moving an existing subscription over A subscription's version is fixed at creation — [Update Webhook Subscription](https://docs.leat.com/v3/oauth/webhooks.md#update-webhook-subscription) cannot change it. To move an endpoint to V2, send the create call again for the same event type with `version: "V2"`, then check the `uuid` you get back: - **The same uuid as before** — your app is subscribed once per event type, and that subscription is now on V2. Your endpoint starts receiving batches straight away. - **A new uuid** — you now have two subscriptions on that event type and your endpoint will receive both shapes. Delete the V1 one once you are happy with V2. > **Warning** > > Update your receiver before you flip the version, not after. The next event after the change arrives in the new shape, and a handler that reads `data` off the top level of the body will not find it. While you are porting, one endpoint can serve both: the envelope tells you which is which through `version`, so branch on that and loop over `events` for V2. ## Related - [Webhooks](https://docs.leat.com/v3/oauth/webhooks.md) — The event catalogue and the subscription endpoints - [Webhooks V1](https://docs.leat.com/v3/oauth/webhooks-v1.md) — The single-event envelope you may be porting from Source: https://docs.leat.com/v3/oauth/webhooks-v2 --- # Register API We're in the process of discontinuing our Register API in favour of a single easy API version. As Personal Access Tokens are now also accepted on our OAuth routes, there is no longer any need for a separate Register API. The current Register API endpoints will still be supported for a while, but we do advise you to migrate to the new API version. If you have any questions, don't hesistate to contact us at [dev@leat.com](mailto:dev@leat.com). ## Related - [Error handling](https://docs.leat.com/v3/error-handling.md) — Proper error handling is a crucial part of the integration - [Tutorial: POS](https://docs.leat.com/v3/tutorials/pos.md) — Learn how to integrate Leat with your Point of Sale system Source: https://docs.leat.com/v3/register-api --- # Testing > **Tip** > > As testing is done on the Production environment, take care to not use any actual customer email addresses. If any Automations or Campaigns are activated in your account, the emails will be sent. Testing your integration can be done simply using our Production environment. We advise you create a new Account - as if you were a new Client - so you can test with a clean dashboard without muddling up actual data with test data. ## Related - [Error handling](https://docs.leat.com/v3/error-handling.md) — Proper error handling is a crucial part of the integration - [Tutorial: POS](https://docs.leat.com/v3/tutorials/pos.md) — Learn how to integrate Leat with your Point of Sale system Source: https://docs.leat.com/v3/testing --- # Kiosk ## Introduction This guide covers how to build a loyalty integration for self-ordering kiosk software. Kiosks differ from traditional POS systems in several key ways: customers interact directly with the interface, input methods are limited (no keyboard for email entry), and the flow is typically self-service. The flows below describe the typical integration patterns, but the exact implementation depends on your hardware and software capabilities. ### In this tutorial - [1\. Main Loyalty Flow](#main-loyalty-flow)— Identify customers, view rewards & vouchers, redeem offers - [2\. Scan Voucher Flow](#scan-voucher-flow)— Scan and redeem vouchers directly without identification - [3\. Pay with Giftcard Flow](#pay-giftcard-flow)— Accept giftcard payments at checkout - [4\. Pay with Prepaid Credit Flow](#pay-prepaid-flow)— Accept prepaid balance payments at checkout ## Main Loyalty Flow This flow covers the core loyalty experience: identifying customers, displaying their rewards, and processing redemptions. Hover over the interactive previews to see which API endpoints are used at each step. #### 1. Identifying Customers On a kiosk, customers identify themselves by scanning their loyalty card. Best practice is to prompt customers directly at the start screen of the kiosk. **Try it:** Click the **Next** button on the kiosk screen to simulate a successful card scan. There should be a button or prompt for a customer to scan their member card, preferably accessible at all times. **Endpoints for this step** - [Find Contact Identifier](https://docs.leat.com/v3/oauth/contact-identifiers.md#find-contact-identifier) Unlike POS systems, entering email addresses is impractical on kiosk hardware, so identification is typically limited to scanning a QR code or entering a loyalty card number. #### 2. Customer Overview Once identified, the customer sees their loyalty information: tier status, point balance, available offers (vouchers and collectable rewards), and redeemable rewards. Hover over each section to see which API endpoints provide this data. **Endpoints for this step** - [Find Contact](https://docs.leat.com/v3/oauth/contacts.md#find-contact) - [Get Credit Balance](https://docs.leat.com/v3/oauth/contacts.md#get-credit-balance) - [Get Tier](https://docs.leat.com/v3/oauth/contacts.md#get-tier) - [List Vouchers](https://docs.leat.com/v3/oauth/vouchers.md#list-vouchers) - [List Collectable Rewards](https://docs.leat.com/v3/oauth/collectable-rewards.md#list-collectable-rewards) - [List Rewards](https://docs.leat.com/v3/oauth/rewards.md#list-rewards) **Displaying Rewards:** Rewards are typically displayed as a list ordered ascending by `required_credits`, showing the reward title and required points. - Show a "Redeem" button for each reward - Disable the button if the customer has insufficient points - On kiosks, consider larger touch targets for easier interaction #### 3. Redeeming Rewards When a customer selects a reward, they see a confirmation screen. This is important on self-service kiosks to prevent accidental redemptions. **Endpoints for this step** - [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception) There are two common approaches for handling reward redemption. Neither is inherently better — the right choice depends on your Kiosk system's capabilities. ##### Option 1: Redeem → Reverse on Failure 1. [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception) — subtracts points immediately 2. Process payment 3. On success: done! 4. On failure: [Reverse Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#reverse-reward-reception) Best when your Kiosk can easily handle failed orders but lacks post-success hooks. ##### Option 2: Pre-Redeem → Collect on Success 1. [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception) with `reward.pre_redeemable: true` — creates a Collectable Reward 2. Process payment 3. On success: [Collect Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md#collect-reward) 4. On failure: reward remains collectable for later Best when your Kiosk can easily trigger an API call after a successful order. Allows customers to save rewards for later visits. #### 4. Redeeming Offers When a customer selects a voucher or collectable reward, they see a confirmation screen. This is important on self-service kiosks to prevent accidental redemptions. **Endpoints for this step** - [Check Voucher Validity](https://docs.leat.com/v3/oauth/vouchers.md#validate-voucher) - [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) For kiosks, we recommend the **Lock → Redeem** approach: 1. [Check Voucher Validity](https://docs.leat.com/v3/oauth/vouchers.md#validate-voucher) , then [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) — reserves the voucher when customer confirms 2. Customer completes their order and pays 3. On success: [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) 4. On failure/timeout: [Release Voucher](https://docs.leat.com/v3/oauth/vouchers.md#release-voucher) (or let it expire automatically) This prevents vouchers from being permanently consumed if the customer abandons their order. #### 4. Offers Applied After payment, show the customer how many points they earned. When the customer was identified via their loyalty card, you can directly award points to their account. **Endpoints for this step** - [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) - [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) - [Redeem Collectable Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md#collect-reward) #### 5. Order Paid After payment, show the customer how many points they earned. When the customer was identified via their loyalty card, you can directly award points to their account. **Endpoints for this step** - [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) - [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) The response from the [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) endpoint includes the points earned, which you can display to the customer. If a voucher was used, make sure to call [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) after the order is successfully placed to finalize the redemption. When creating Orders, the best practice is to create the Order as completely as you can. This includes linking Products and any Leat-actions that may have been undertaken in the Order process (e.g. Reward Redemptions, Voucher Redemptions, Giftcard Transactions etc.). Some best practices to take into account: - `external_identifier: ` This is the main identifier from your system, usually a UUID or GUID or some other. It's imperative this is unique across all Orders. - `order_amount, total_order_amount, total_discount_amount: ` These are mainly for visual and reference purposes. When Orders are using in Order Actions (to make Points Transactions, for instance), these values will be largely ignored. To account for product-specific exclusions and rules, the order amounts used in calculations and actions will be calculated using the line items, charges and discounts. Still, please ensure these match the content to prevent any confusion. - `line_items.external_identifier:` This is the unique identifier of this line item. Another line item of the same product should have their own unique identifier. - `line_items.total_amount:` The total amount of a line items ought to be the line items' price times the quantity, minus the discount amount. The total amount does not include the total amounts of any sub line items. They are counted separately. - `line_items.product:` For a host of features in Leat, it's imperative the product is linked (if present). The external identifier counts as the main unique identifier of the product, which is of course static for the same product across multiple line items and orders. If no product yet exists for the external identifier, one will be created. However, to fully enable the Product filtering feature, it's highly recommended to create the entire [product library](https://docs.leat.com/v3/oauth/products.md#create-product), including [categories](https://docs.leat.com/v3/oauth/categories.md#create-category), upon integration installation. - `line_items.sub_line_items:` Line items may include sub line items, like modifiers. **Note:** the total amounts of the sub line items count separately from the line item's total amount. Example: a line item for a 'Pizza Margherita' with a total amount of 1200 ($12) may include a sub line item for 'Extra Cheese' with a total amount of 200 ($2), which would bring the total order amount to 1400 ($14). The $2 from the sub line item is *not* included in the total amount on the parent line item. Sub line items are to be linked to products in the same way as their parent line items. - `line_items.result:` This is the 'Leat result' of this line item, if any exists. Examples of this is when a customer buys a Gift card or a Voucher. This would be reflected as a line item on the Order and the resulting Giftcard Transaction (usually created after payment has succeeded) can be linked as a result. Highly recommended to link if present. - `applied_discounts:` If one or more discounts have been applied to the order, these should be added here. Discounts are either applied on an Order level, or on a (Sub)Line Item level. In the latter, the applied discount should also be reflected in the discount amount on the individual (sub) line item(s) and/or charges. In such a case, the specific items should also be linked to the discount via the line items, sub line items and charges parameters. - `applied_discounts.offer:` If the applied discount was the result of a Leat offer (e.g. Reward Redemption, Voucher Redemption etc.), this offer should be added to the discount. Highly recommended to link if present. - `charges:` Charges are costs that are applied after (sub) line items. Examples include gratuity, service charges, shipping, wrapping or taxes (all of these are default Charge Definitions). In the case of taxes, there are only a few use cases in which taxes are to be included as separate charges. Most often, it's preferable to include them in the line items price instead of as separate charges. Charge Definitions are what Products are to Line Items. - `payments:` Currently, only Leat-specific payments can be added here. Payment methods like credit card or cash are not to be sent in. If (part of) the order was paid using Leat payment methods (Giftcards, Prepaid), these should be linked here. If you have any questions about Orders, please reach out to your partnership manager or contact us at dev@leat.com. Applying discounts to the order is often the most complex part of the integration. To enable your kiosk software to handle this correctly, you'll need to create custom attributes that define the discount logic. **Important:** Custom attributes must be created on the correct entity: - **For Rewards:** Create custom attributes on the **Rewards** entity - **For Vouchers:** Create custom attributes on the **Promotions** entity (the parent of Vouchers) **Common custom attributes:** - `discount_type` - Dropdown: *percentage\_discount*, *absolute\_discount*, or *free\_item* - `discount_value` - Number field (e.g., 100 for 100% off = free item, or 5 for €5 off) - `product_code` - SKU of the specific product - `category` - If the reward applies to any product in a category When an order is cancelled or returned, you'll need to correct the loyalty data. This involves correcting issued points and reversing any redeemed vouchers. ##### 1\. Correcting Issued Points Use the [Create and Process Order Return](https://docs.leat.com/v3/oauth/order-returns.md#create-and-process-order-return) endpoint to subtract points that were originally awarded. ##### 2\. Reversing Redeemed Vouchers Use [Reverse Voucher Redemption](https://docs.leat.com/v3/oauth/vouchers.md#reverse-voucher-redemption) to restore the voucher to its original state. ### Scan Voucher Directly Customers can scan a voucher QR code directly at the kiosk without first identifying themselves. This is useful for promotional vouchers received via email, flyers, or printed coupons. #### 1. Trigger Voucher Scan Sometime during the ordering process, allow the customer to add a voucher. This is often done during the check-out step, but could also be at any other point before payment. **Endpoints for this step** - [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) - [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) - [Redeem Collectable Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md#collect-reward) This flow is typically used when a customer has a voucher code from an external source (email campaign, flyer, receipt) and wants to redeem it using the Voucher's QR-code. **Try it:** Click the `Add Voucher` button underneath the order details to start the voucher scanning flow. #### 2. Scan Voucher QR Code The customer scans the voucher QR code or enters the code manually. The system retrieves the voucher details using the [Find Voucher](https://docs.leat.com/v3/oauth/vouchers.md#find-voucher) endpoint. **Endpoints for this step** - [Find Voucher](https://docs.leat.com/v3/oauth/vouchers.md#find-voucher) **Try it:** Hover over the scan area and click to simulate scanning a voucher QR code. #### 3. Apply Voucher The system displays the voucher details and the customer confirms they want to apply it to their order. **Endpoints for this step** - [Check Voucher Validity](https://docs.leat.com/v3/oauth/vouchers.md#validate-voucher) - [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) When the customer clicks "Apply", use [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) to reserve it. After successful payment, call [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) to finalize the redemption. **Note:** The Promotion linked to the Voucher should contain custom attributes (like `discount_type` and `discount_value`) that enable the kiosk to apply the correct discount. Some vouchers may not be linked to a customer account (e.g., promotional vouchers from flyers). When redeeming these: - The voucher can still be redeemed without customer identification - If you want to track the redemption, prompt the customer to scan their loyalty card and pass the `contact_uuid` to the Redeem Voucher API call - Points can still be awarded if the customer identifies themselves during checkout Consider offering to link the voucher to the customer's account for a better experience on future visits. #### 4. Voucher Applied Add the discount logic to the Order. **Endpoints for this step** - [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) - [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) **Note:** If the order wasn't yet linked to a contact at this point, use the contact linked to the Voucher and link it to the order. Before applying a voucher, validate that: - The voucher hasn't expired (check `expires_at`) - The voucher hasn't already been redeemed (check `state`) - The voucher is valid for the current location (if applicable) - The order meets any minimum requirements (e.g., minimum order value) Display clear error messages if the voucher cannot be applied. ### Paying with Giftcards Customers can use their giftcard balance to pay for (part of) their order at the kiosk. This is typically offered as a payment option during checkout. #### 1. Select Giftcard Payment At checkout, the customer selects "Giftcard" as their payment method. This triggers the giftcard scanning flow. **Try it:** Click the `Giftcard` payment button to start the giftcard payment flow. #### 2. Scan Giftcard The customer scans their giftcard QR code or enters the code manually. The system retrieves the giftcard balance using the [Find Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#find-giftcard) endpoint. **Endpoints for this step** - [Find Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#find-giftcard) **Try it:** Hover over the scan area and click to simulate scanning a giftcard. #### 3. Confirm Payment The system displays the giftcard balance and calculates how much will be deducted. The customer confirms the payment. **Endpoints for this step** - [Find Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#find-giftcard) - [Create Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#create-giftcard-transaction) Upon confirmation, the system calls [Create Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#create-giftcard-transaction) with a **negative amount** to deduct from the balance. **Note:** If the giftcard balance doesn't cover the full order, the remaining amount can be paid with another payment method (card, cash, etc.). If the order is cancelled or fails after the giftcard transaction has been created, use the [Reverse Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#reverse-giftcard-transaction) endpoint to restore the giftcard's balance. ### Paying with Prepaid Credit Customers who have prepaid credit on their account can use it to pay for (part of) their order. Since prepaid is tied to their account, the customer must be identified first (typically through the loyalty flow). #### 1. Select Prepaid Payment At checkout, if the customer has been identified and has prepaid credit, the "Prepaid" payment option becomes available. The customer can see their available balance before selecting this option. **Try it:** Click the `Prepaid` payment button to start the prepaid payment flow. **Note:** Unlike giftcards, prepaid credit requires customer identification. On kiosks, this typically happens through the loyalty card scan in the main flow. #### 2. Confirm Payment The system displays the customer's prepaid balance and calculates how much will be deducted. The customer confirms the payment. **Endpoints for this step** - [Get Prepaid Balance](https://docs.leat.com/v3/oauth/contacts.md#get-prepaid-balance) - [Create Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#create-prepaid-transaction) Upon confirmation, the system calls [Create Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#create-prepaid-transaction) with a **negative amount** to deduct from the balance. **Note:** If the prepaid balance doesn't cover the full order, the remaining amount can be paid with another payment method (card, cash, etc.). If the order is cancelled or fails after the prepaid transaction has been created, use the [Reverse Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#reverse-transaction) endpoint to restore the customer's balance. Source: https://docs.leat.com/v3/tutorials/kiosk --- # Migrate to V3 V3 of the public API is now live. There are some breaking changes here. While we will continue to support the V1 & V2 for the time being, at some point new developments in our product will inevitably lead to deprecation of the older versions. Also, newer features – like prepaid API calls and the creation of custom Reward Attributes via API – will only be available in the V3 version and onwards. We therefore urge you to migrate to V3 as soon as you can. ### Summary of breaking changes The V3 version comes with some breaking changes: - **Contact based - replacing Members and Recipients:** The entire API is now Contact based, meaning 'Members' and 'Recipients' no longer play a role anymore and have been replaced with Contacts for all relevant API calls. - **Contact Identifiers - replacing Loyalty cards:** The Loyalty system will no longer be limited to the standard Loyalty cards, so anything unique can be used to identify Contacts. Therefore, although Loyalty cards can still be used, they will now be represented by Contact Identifiers. - **Orders:** Implicitly, all credit receptions were created using 'purchase\_amount'. This has been replaced by the more dynamic and diverse Orders. Clients can create Orders with line items, charges, applied discounts and even link applied reward redemptions, giftcard transactions et cetera. - **Loyalty Tokens - replacing Staged Credit Receptions:** A new process for creating Staged Credit Receptions has been implemented, which doesn't require any API calls. This way is quicker and independent of a stable internet connection, and has been rebranded 'Loyalty Tokens'. - **Single Rewards list and reception calls:** Instead of getting three separate lists of Rewards, as well as having to do a separate 'Create Reward Reception' API call per Reward type, you now get one unified list of Rewards and only need one reception call for all types. - **Use of UUIDs:** The new API is based mostly on UUIDs, instead of the simpler IDs used in the past. One or two API calls still use regular IDs, but they will be refactored to UUIDs soon enough, so keep that in mind. ## Refactoring processes Here we'll give a short guide on how to migrate all the old processes to the new v3 versions. If you're retrieving Member information using the public API, you will now need to retrieve Contacts instead. The new API calls are self-evident enough, but keep a couple of things in mind. Firstly, we now use UUIDs, which are strings, instead of the previous numeric IDs. Secondly, the responses for retrieving Contact Information– primarily how custom attributes are returned – have changed a bit. Previously, a Loyalty card ID or hash was used to retrieve a Member or perform a credit reception or reward reception. Now, as we'll soon accept virtually anything as an identifier, this has been expanded a bit. Instead of the 'hash', we'll now be using the 'contact\_identifier\_value', which can of course still represent a hash. The loyalty\_card\_id will not be used at all, nor will it be replaced by a contact identifier ID. ### Contacts Instead of the 'Get Member' API call, implement the new 'Create or Find Contact' API call, and then use the **UUID** returned in the response. Instead of the 'Find Loyalty Card by Hash' to retrieve the Member information, implement the 'Find Contact Identifier' API call. Again, use the Contact's **UUID** from the response. Or directly use the Loyalty card hash as the 'contact\_identifier\_value' in the new Create Credit Reception API call (make sure to do proper error handling in case it is not yet linked to a contact). ### Orders Instead of creating 'Credit Receptions', you can now create a full 'Order' and have the system process it for you. This will calculate the points for you. Rewards are now returned as a single list, instead of a separate list for each reward type, which should make life easier. If for some reason you still want to separate the types, you can do so using the 'reward\_type' attribute. You can now create Reward attributes using the API. This way, when a Client enables their integration in the backoffice, an API call can be done to create all necessary attributes (if they exist), such as PLU or discount code. Now, the Reward's **UUID** instead of ID is returned and used in subsequent API calls. Also make sure you use the Contact's UUID instead of the Member ID and the Shop's UUID instead of the Shop's ID, if needed. Reward Receptions can now be conducted using a single API calls, instead of an API call per reward type. Beware that you now need to use the Reward's **UUID** rather than its ID, and Contact Identifiers instead of Loyalty cards. Make sure you use the Contact's UUID instead of the Member ID and the Shop's UUID instead of the Shop's ID, if needed. Currently, the API calls concerning Giftcards and Giftcard Transactions are unchanged. However, where an ID is used (Giftcard ID, Giftcard Program ID, Giftcard Transaction ID), this will soon be refactored to UUIDs as well. ## Related - [Error handling](https://docs.leat.com/v3/error-handling.md) — Proper error handling is a crucial part of the integration - [Webhooks](https://docs.leat.com/v3/oauth/webhooks.md) — Learn about our new webhooks system Source: https://docs.leat.com/v3/tutorials/migrate-to-v3 --- # Order at Table ## Introduction This guide covers how to build a loyalty integration for Order at Table systems. These are typically mobile web applications where customers scan a QR code at their table and order directly from their own device. Order at Table integrations differ from kiosks and POS systems in several key ways: customers use their own mobile devices, authentication is typically email-based (passwordless), and the experience should feel native to mobile browsing. ### In this tutorial - [1\. Adding Blocks to Page](#oat-menu-start)— Display loyalty prompts on the menu page - [2\. Identifying Customer](#oat-member-login)— Email-based login, verification, and sign up - [3\. Showcasing Rewards and Vouchers](#oat-menu-logged-in)— Display available offers to logged-in customers - [4\. Redeeming Offers](#oat-redeem-voucher)— Lock and redeem vouchers and rewards - [5\. Finalising the Order](#oat-basket)— Basket with applied offers - [6\. Order Successful](#oat-order-success)— Display earned points after payment ## Main Loyalty Flow This flow covers the complete Order at Table loyalty experience: from displaying offers to non-authenticated users, through login/signup, to redeeming offers and completing orders. Hover over the interactive previews to see which API endpoints are used at each step. #### 1. Adding Blocks to Page In an Order at Table integration, customers browse and order from their own mobile device. The loyalty integration should be seamlessly embedded into the ordering experience, with an offers section prominently displayed on the menu page. For non-authenticated users, show a prompt to join the loyalty program or log in to access their rewards and offers. **Try it:** Click the **"Start earning rewards now!"** banner to simulate a customer wanting to log in. #### 2. Identifying Customer Unlike kiosks where customers scan a card, Order at Table integrations typically use email-based authentication. When a customer enters their email, first check if they already exist in the system. **Endpoints for this step** - [Find Contact](https://docs.leat.com/v3/oauth/contacts.md#find-one-by) **Authentication Flow:** Use your own authentication layer if you have your own, otherwise, follow these steps below 1. Customer enters their email address 2. Call [Find Contact](https://docs.leat.com/v3/oauth/contacts.md#find-one-by) to check if the email exists 3. **If found:** Send verification code via [Send Contact Verification Email](https://docs.leat.com/v3/oauth/contact-verification.md#send) 4. **If not found:** Redirect to sign up form #### 2. Verification Code For existing customers, send a verification code to their email. This provides a passwordless authentication experience that's secure and user-friendly on mobile devices. **Endpoints for this step** - [Send Contact Verification Email](https://docs.leat.com/v3/oauth/contact-verification.md#send) - [Verify Login Code](https://docs.leat.com/v3/oauth/contact-verification.md#verify) If your Order at Table system already has its own authentication layer (e.g., customers log in to your app), you can skip this verification step and directly link the session to the customer's contact record using their authenticated email or user ID. #### 2. Sign Up (New Customers) For new customers, collect their basic information to create a loyalty account. Keep the form simple to reduce friction. **Endpoints for this step** - [Create Contact](https://docs.leat.com/v3/oauth/contacts.md#create-contact) **Recommended fields:** - Email (pre-filled from login attempt - required) - First name (optional but highly recommended) - Last name (optional but highly recommended) - Birthdate (optional - useful for birthday rewards) - Terms & Conditions acceptance #### 3. Showcasing Rewards and Vouchers Once logged in, the customer sees their available offers directly on the menu page. This includes vouchers and collectable rewards that can be redeemed with their order. **Endpoints for this step** - [List Vouchers](https://docs.leat.com/v3/oauth/vouchers.md#list-vouchers) - [List Collectable Rewards](https://docs.leat.com/v3/oauth/collectable-rewards.md#list-collectable-rewards) - [List Rewards](https://docs.leat.com/v3/oauth/rewards.md#list-rewards) **Displaying Offers:** - Show the customer's current point balance - Display available vouchers with expiry dates - Show collectable rewards (pre-redeemed rewards) - Both vouchers and collectable rewards can be redeemed free of charge - Provide a "See all" link to view all available offers #### 3. All Offers View The "All offers" screen shows both available offers (vouchers and collectable rewards) and purchasable rewards that customers can redeem with their points. **Endpoints for this step** - [List Vouchers](https://docs.leat.com/v3/oauth/vouchers.md#list-vouchers) - [List Collectable Rewards](https://docs.leat.com/v3/oauth/collectable-rewards.md#list-collectable-rewards) - [List Rewards](https://docs.leat.com/v3/oauth/rewards.md#list-rewards) ##### Vouchers Pre-issued offers that customers receive through promotions, campaigns, or as gifts. Can be redeemed for free. ##### Collectable Rewards Rewards that were pre-redeemed (points already deducted) but not yet collected. Can be redeemed for free. ##### Rewards Items that customers can purchase with their points. Requires sufficient point balance to redeem. #### 4. Redeeming Offers When a customer selects an offer, show a confirmation screen before applying it to their order. This prevents accidental redemptions. **Endpoints for this step** - [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception) - [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) For Order at Table, we recommend the **Lock → Redeem** approach: 1. [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) — reserves the voucher when customer confirms 2. Customer completes their order and pays 3. On success: [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) 4. On failure/timeout: [Release Voucher](https://docs.leat.com/v3/oauth/vouchers.md#release-voucher) (or let it expire automatically) This prevents vouchers from being permanently consumed if the customer abandons their order. Applying discounts to the order is often the most complex part of the integration. To enable your Order at Table system to handle this correctly, you'll need to create custom attributes that define the discount logic. **Important:** Custom attributes must be created on the correct entity: - **For Rewards:** Create custom attributes on the **Rewards** entity - **For Vouchers:** Create custom attributes on the **Promotions** entity (the parent of Vouchers) **Common custom attributes:** - `discount_type` - Dropdown: *percentage\_discount*, *absolute\_discount*, or *free\_item* - `discount_value` - Number field (e.g., 100 for 100% off = free item, or 5 for €5 off) - `product_code` - SKU of the specific product - `category` - If the reward applies to any product in a category #### 4. Offer Applied After confirming, show a success message and add the discounted item to the customer's basket. #### 5. Finalising the Order The basket shows all items including any applied offers. Customers can still add more offers before checking out. **Endpoints for this step** - [List Vouchers](https://docs.leat.com/v3/oauth/vouchers.md#list-vouchers) - [List Collectable Rewards](https://docs.leat.com/v3/oauth/collectable-rewards.md#list-collectable-rewards) - [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) - [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) - [Collect Collectable Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md#collect-reward) **Basket Display:** - Show original price crossed out for discounted items - Display the offer name that was applied - Keep offers section visible for additional redemptions - Show updated total reflecting all discounts #### 6. Order Successful After successful payment, show the customer how many points they earned from their order. **Endpoints for this step** - [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) The response from the [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) endpoint includes the points earned, which you can display to the customer. Make sure to call [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) and/or [Collect Collectable Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md#collect-reward) after successful payment to finalize any applied offers. When creating Orders, the best practice is to create the Order as completely as you can. This includes linking Products and any Leat-actions that may have been undertaken in the Order process (e.g. Reward Redemptions, Voucher Redemptions, Giftcard Transactions etc.). Some best practices to take into account: - `external_identifier: ` This is the main identifier from your system, usually a UUID or GUID or some other. It's imperative this is unique across all Orders. - `order_amount, total_order_amount, total_discount_amount: ` These are mainly for visual and reference purposes. When Orders are using in Order Actions (to make Points Transactions, for instance), these values will be largely ignored. To account for product-specific exclusions and rules, the order amounts used in calculations and actions will be calculated using the line items, charges and discounts. Still, please ensure these match the content to prevent any confusion. - `line_items.external_identifier:` This is the unique identifier of this line item. Another line item of the same product should have their own unique identifier. - `line_items.total_amount:` The total amount of a line items ought to be the line items' price times the quantity, minus the discount amount. The total amount does not include the total amounts of any sub line items. They are counted separately. - `line_items.product:` For a host of features in Leat, it's imperative the product is linked (if present). The external identifier counts as the main unique identifier of the product, which is of course static for the same product across multiple line items and orders. If no product yet exists for the external identifier, one will be created. However, to fully enable the Product filtering feature, it's highly recommended to create the entire [product library](https://docs.leat.com/v3/oauth/products.md#create-product), including [categories](https://docs.leat.com/v3/oauth/categories.md#create-category), upon integration installation. - `line_items.sub_line_items:` Line items may include sub line items, like modifiers. **Note:** the total amounts of the sub line items count separately from the line item's total amount. Example: a line item for a 'Pizza Margherita' with a total amount of 1200 ($12) may include a sub line item for 'Extra Cheese' with a total amount of 200 ($2), which would bring the total order amount to 1400 ($14). The $2 from the sub line item is *not* included in the total amount on the parent line item. Sub line items are to be linked to products in the same way as their parent line items. - `line_items.result:` This is the 'Leat result' of this line item, if any exists. Examples of this is when a customer buys a Gift card or a Voucher. This would be reflected as a line item on the Order and the resulting Giftcard Transaction (usually created after payment has succeeded) can be linked as a result. Highly recommended to link if present. - `applied_discounts:` If one or more discounts have been applied to the order, these should be added here. Discounts are either applied on an Order level, or on a (Sub)Line Item level. In the latter, the applied discount should also be reflected in the discount amount on the individual (sub) line item(s) and/or charges. In such a case, the specific items should also be linked to the discount via the line items, sub line items and charges parameters. - `applied_discounts.offer:` If the applied discount was the result of a Leat offer (e.g. Reward Redemption, Voucher Redemption etc.), this offer should be added to the discount. Highly recommended to link if present. - `charges:` Charges are costs that are applied after (sub) line items. Examples include gratuity, service charges, shipping, wrapping or taxes (all of these are default Charge Definitions). In the case of taxes, there are only a few use cases in which taxes are to be included as separate charges. Most often, it's preferable to include them in the line items price instead of as separate charges. Charge Definitions are what Products are to Line Items. - `payments:` Currently, only Leat-specific payments can be added here. Payment methods like credit card or cash are not to be sent in. If (part of) the order was paid using Leat payment methods (Giftcards, Prepaid), these should be linked here. If you have any questions about Orders, please reach out to your partnership manager or contact us at dev@leat.com. Applying discounts to the order is often the most complex part of the integration. To enable your Order at Table system to handle this correctly, you'll need to create custom attributes that define the discount logic. **Important:** Custom attributes must be created on the correct entity: - **For Rewards:** Create custom attributes on the **Rewards** entity - **For Vouchers:** Create custom attributes on the **Promotions** entity (the parent of Vouchers) **Common custom attributes:** - `discount_type` - Dropdown: *percentage\_discount*, *absolute\_discount*, or *free\_item* - `discount_value` - Number field (e.g., 100 for 100% off = free item, or 5 for €5 off) - `product_code` - SKU of the specific product - `category` - If the reward applies to any product in a category When an order is cancelled or returned, you'll need to correct the loyalty data. This involves correcting issued points and reversing any redeemed vouchers. ##### 1\. Correcting Issued Points Use the [Create and Process Order Return](https://docs.leat.com/v3/oauth/order-returns.md#create-and-process-order-return) endpoint to subtract points that were originally awarded. ##### 2\. Reversing Redeemed Vouchers Use [Reverse Voucher Redemption](https://docs.leat.com/v3/oauth/vouchers.md#reverse-voucher-redemption) to restore the voucher to its original state. Source: https://docs.leat.com/v3/tutorials/order-at-table --- # Point of Sale ## Introduction This is a guide on how to build an integration for POS software. The flows describe the typical flow, but much depends on possibilities and limitations offered by both hardware and software involved. In this tutorial, we assume you'll build the UI yourself, leveraging our API endpoints to feed the flows with the necessary data. However, there is also the option to use Portal Sessions to power your POS integration. This involves using either the [Portal Sessions API](https://docs.leat.com/v3/oauth/portal-sessions.md) or the [Portal Sessions SDK](https://www.npmjs.com/package/@leatcom/portal-sessions). This option is sometimes used for a quick launch strategy or prototyping, while a more polished API-based approach is built in the mean time. ### In this tutorial - [1\. Main Loyalty Flow](#identification)— Identify customers, view rewards & vouchers, redeem offers - [2\. Scan Voucher Flow](#scan-voucher-flow)— Scan and redeem vouchers directly - [3\. Buy Giftcard Flow](#buy-giftcard-flow)— Sell and activate physical giftcards - [4\. Pay with Giftcard Flow](#pay-giftcard-flow)— Accept giftcard payments - [5\. Buy Prepaid Credit Flow](#buy-prepaid-flow)— Top up customer Prepaid balance - [6\. Pay with Prepaid Credit Flow](#pay-prepaid-flow)— Accept Prepaid balance payments #### 1. Identifying Customers On the POS, a button will need to be added to trigger the identification of the customer and subsequent loyalty flows. Where this button is placed and how it looks is up to you, it's usually accompanied by a label stating 'Loyalty' or 'Rewards' or similar. When the button is clicked, the POS will open a modal or a separate screen where the customer can be identified. This could be done by scanning a QR code, entering a loyalty card number or entering an email address. Scanning a QR-code is often quickest and most convenient. **Try it:** Click the green `Rewards` button on the POS screen to start the identification flow. #### 1. Scanning the Customer's Card After clicking the rewards button, the customer needs to identify themselves. There are three common methods for this, depending on your hardware capabilities: Camera scanner Barcode/QR scanner Keyboard **Endpoints for this step** - [Find Contact Identifier](https://docs.leat.com/v3/oauth/contact-identifiers.md#find-contact-identifier) - **Camera Scanner:** Uses the device camera to scan QR codes - ideal for tablets and modern POS systems - **Barcode/QR Scanner:** Uses dedicated hardware scanners - fastest and most reliable for high-volume environments - **Keyboard Entry:** Manual entry of card number or email - works on any system as a fallback **Try it:** Hover over the scan area and click to simulate a successful scan. **Tip:** You can also use [Find or Create Contact](https://docs.leat.com/v3/oauth/contacts.md#find-or-create-contact) to identify customers by email address - useful when they've forgotten their loyalty card, though slower. #### 2. Customer Overview Once the customer is identified, you'll display their loyalty information. This typically includes their tier status, point balance, available offers (combination of Vouchers and Collectable Rewards), and redeemable rewards. Hover over each section to see which API endpoints provide this data. **Endpoints for this step** - [Find Contact](https://docs.leat.com/v3/oauth/contacts.md#find-contact) - [Get Credit Balance](https://docs.leat.com/v3/oauth/contacts.md#get-credit-balance) - [Get Tier](https://docs.leat.com/v3/oauth/contacts.md#get-tier) - [List Vouchers](https://docs.leat.com/v3/oauth/vouchers.md#list-vouchers) - [List Collectable Rewards](https://docs.leat.com/v3/oauth/collectable-rewards.md#list-collectable-rewards) - [List Rewards](https://docs.leat.com/v3/oauth/rewards.md#list-rewards) **Displaying Rewards:** Rewards are typically displayed as a list ordered ascending by `required_credits`, showing the reward title, required points, and sometimes image. - Show a "Redeem" button for each reward - Disable the button if the customer has insufficient points - Optionally show a progress bar indicating how many more points they need **Displaying Offers:** Rewards are typically fewer, therefore displayed as a grid or horizontal list, showing title, expiration date (if present), and sometimes image. #### 3. Redeeming Rewards When a customer selects a reward from their available options, you'll show a confirmation screen. This allows them to confirm they want to spend their points on this reward before it's applied to their order. **Endpoints for this step** - [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception) **Note:** Applying discount logic to the cart is often the most complex part of the integration. See the [Custom Attributes](#custom-attributes) section for how to configure discount logic. There are two common approaches for handling reward redemption. Neither is inherently better — the right choice depends on your POS system's capabilities. ##### Option 1: Redeem → Reverse on Failure 1. [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception) — subtracts points immediately 2. Process payment 3. On success: done! 4. On failure: [Reverse Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#reverse-reward-reception) Best when your POS can easily handle failed orders but lacks post-success hooks. ##### Option 2: Pre-Redeem → Collect on Success 1. [Create Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#create-reward-reception) with `reward.pre_redeemable: true` — creates a Collectable Reward 2. Process payment 3. On success: [Collect Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md#collect-reward) 4. On failure: reward remains collectable for later Best when your POS can easily trigger an API call after a successful order. Allows customers to save rewards for later visits. #### 4. Redeeming Offers Similarly, when a customer wants to use a voucher or collectable reward (like a birthday offer), they'll see a confirmation screen. These offers could have conditions like minimum spend requirements and expiration dates. **Endpoints for this step** - [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) - [Redeem Collectable Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md#collect-reward) **Note:** Applying discount logic to the cart is often the most complex part of the integration. See the [Custom Attributes](#custom-attributes) section for how to configure discount logic. There are two common approaches for handling voucher redemption. Neither is inherently better — the right choice depends on your POS system's capabilities. ##### Option 1: Lock → Redeem on Success 1. [Check Voucher Validity](https://docs.leat.com/v3/oauth/vouchers.md#validate-voucher) , then [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) — reserves the voucher 2. Process payment 3. On success: [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) 4. On failure: [Release Voucher](https://docs.leat.com/v3/oauth/vouchers.md#release-voucher) (or let it expire) Best when your POS can easily trigger an API call after a successful order. ##### Option 2: Redeem → Reverse on Failure 1. [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) — immediately redeems 2. Process payment 3. On success: done! 4. On failure: [Reverse Voucher Redemption](https://docs.leat.com/v3/oauth/vouchers.md#reverse-voucher-redemption) Best when your POS can easily handle failed orders but lacks post-success hooks. #### 5. Checkout with Rewards Applied After rewards and offers are confirmed, they're applied to the order. The checkout screen shows the discounts applied (highlighted in green) and the updated total. This is where you'll process the final payment and record the transaction. **Endpoints for this step** - [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) - [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) - [Redeem Collectable Reward](https://docs.leat.com/v3/oauth/collectable-rewards.md#collect-reward) #### 6. Order Complete After payment, show the customer how many points they earned from this transaction. This reinforces the value of the loyalty program and encourages future visits. Clients often want to display the points earned and new balance on the receipt. The response from the [Create and Process Order](https://docs.leat.com/v3/oauth/orders.md#create-and-process-order) endpoint includes this information. When creating Orders, the best practice is to create the Order as completely as you can. This includes linking Products and any Leat-actions that may have been undertaken in the Order process (e.g. Reward Redemptions, Voucher Redemptions, Giftcard Transactions etc.). Some best practices to take into account: - `external_identifier: ` This is the main identifier from your system, usually a UUID or GUID or some other. It's imperative this is unique across all Orders. - `order_amount, total_order_amount, total_discount_amount: ` These are mainly for visual and reference purposes. When Orders are using in Order Actions (to make Points Transactions, for instance), these values will be largely ignored. To account for product-specific exclusions and rules, the order amounts used in calculations and actions will be calculated using the line items, charges and discounts. Still, please ensure these match the content to prevent any confusion. - `line_items.external_identifier:` This is the unique identifier of this line item. Another line item of the same product should have their own unique identifier. - `line_items.total_amount:` The total amount of a line items ought to be the line items' price times the quantity, minus the discount amount. The total amount does not include the total amounts of any sub line items. They are counted separately. - `line_items.product:` For a host of features in Leat, it's imperative the product is linked (if present). The external identifier counts as the main unique identifier of the product, which is of course static for the same product across multiple line items and orders. If no product yet exists for the external identifier, one will be created. However, to fully enable the Product filtering feature, it's highly recommended to create the entire [product library](https://docs.leat.com/v3/oauth/products.md#create-product), including [categories](https://docs.leat.com/v3/oauth/categories.md#create-category), upon integration installation. - `line_items.sub_line_items:` Line items may include sub line items, like modifiers. **Note:** the total amounts of the sub line items count separately from the line item's total amount. Example: a line item for a 'Pizza Margherita' with a total amount of 1200 ($12) may include a sub line item for 'Extra Cheese' with a total amount of 200 ($2), which would bring the total order amount to 1400 ($14). The $2 from the sub line item is *not* included in the total amount on the parent line item. Sub line items are to be linked to products in the same way as their parent line items. - `line_items.result:` This is the 'Leat result' of this line item, if any exists. Examples of this is when a customer buys a Gift card or a Voucher. This would be reflected as a line item on the Order and the resulting Giftcard Transaction (usually created after payment has succeeded) can be linked as a result. Highly recommended to link if present. - `applied_discounts:` If one or more discounts have been applied to the order, these should be added here. Discounts are either applied on an Order level, or on a (Sub)Line Item level. In the latter, the applied discount should also be reflected in the discount amount on the individual (sub) line item(s) and/or charges. In such a case, the specific items should also be linked to the discount via the line items, sub line items and charges parameters. - `applied_discounts.offer:` If the applied discount was the result of a Leat offer (e.g. Reward Redemption, Voucher Redemption etc.), this offer should be added to the discount. Highly recommended to link if present. - `charges:` Charges are costs that are applied after (sub) line items. Examples include gratuity, service charges, shipping, wrapping or taxes (all of these are default Charge Definitions). In the case of taxes, there are only a few use cases in which taxes are to be included as separate charges. Most often, it's preferable to include them in the line items price instead of as separate charges. Charge Definitions are what Products are to Line Items. - `payments:` Currently, only Leat-specific payments can be added here. Payment methods like credit card or cash are not to be sent in. If (part of) the order was paid using Leat payment methods (Giftcards, Prepaid), these should be linked here. If you have any questions about Orders, please reach out to your partnership manager or contact us at dev@leat.com. Applying discounts to the cart/order is often the most complex part of the integration. To enable your POS to handle this correctly, you'll need to create custom attributes that define the discount logic. **Important:** Custom attributes must be created on the correct entity: - **For Rewards:** Create custom attributes on the **Rewards** entity - **For Vouchers:** Create custom attributes on the **Promotions** entity (the parent of Vouchers) **Naming Convention:** We highly recommend adding an integration-based **prefix** (or suffix) to each custom attribute's internal name to ensure uniqueness. For example: `mypos_discount_type` instead of just `discount_type`. This prevents errors due to duplicate attribute names, as other integrating parties might try to create attributes with the same names. There are two common approaches for configuring discount logic: ##### Option 1: Reference ID `discount_rule_id` - Reference to an existing discount/price rule in your POS system, often as a single select attribute ##### Option 2: Direct Configuration Build the discount logic with a set of attributes fully (or partially) in Leat **Common custom attributes for Option 2:** - `discount_type` - Dropdown: *percentage\_discount*, *absolute\_discount*, or *free\_item* - `discount_value` - Number field (e.g., 100 for 100% off = free item, or 5 for £5 off) - `product_code` - SKU of the specific product (used in ~80% of rewards). Can be comma-separated for multiple options - `category` - If the reward applies to any product in a category rather than a specific SKU These attribute values are returned in the API responses when listing and claiming rewards, or redeeming vouchers, allowing your POS to apply the correct discount. If the customer hasn't identified themselves during the order flow, you can still offer them the opportunity to collect loyalty points after payment. This is useful for customers who forgot to scan their card or decided to join the loyalty program at checkout. After a successful payment, prompt the customer: *"Would you like to collect loyalty points on this order?"* - If they choose **Yes**, show the scan identifier screen and link the transaction to their account - If they choose **No**, optionally print a QR code on the receipt that allows them to claim points later **Tip:** You can generate a Loyalty Token URL without an API call to print on receipts. See our documentation for more details on implementing this feature. When an order is cancelled or (partially) returned, you'll need to correct the loyalty data. This involves two separate concerns: correcting issued points, and reversing any redeemed rewards, vouchers, or payment transactions. ##### 1\. Correcting Issued Points To correct points that were issued for a cancelled or returned order, use the [Create and Process Order Return](https://docs.leat.com/v3/oauth/order-returns.md#create-and-process-order-return) endpoint. This will subtract the points that were originally awarded for the returned items. For partial returns, only include the returned line items — the system will calculate the correct point deduction. ##### 2\. Reversing Redeemed Rewards & Vouchers If rewards or vouchers were redeemed as part of the order, these need to be reversed separately. Each type has its own reversal endpoint: - [Reverse Reward Reception](https://docs.leat.com/v3/oauth/reward-receptions.md#reverse-reward-reception) — restores the points and marks the reward as unredeemed - [Reverse Voucher Redemption](https://docs.leat.com/v3/oauth/vouchers.md#reverse-voucher-redemption) — restores the voucher to its original state - [Reverse Reward Collection](https://docs.leat.com/v3/oauth/collectable-rewards.md#reverse-collectable-reward-redemption) — restores a collected reward back to collectable status ##### 3\. Reversing Payment Transactions If the order was (partially) paid with giftcard balance or prepaid credit, those transactions also need to be reversed: - [Reverse Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#reverse-giftcard-transaction) — refunds the amount back to the giftcard - [Reverse Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#reverse-prepaid-transaction) — refunds the amount back to the customer's prepaid balance **💡 Tip:** Store the transaction UUIDs (reward reception UUID, voucher redemption UUID, giftcard transaction UUID, prepaid transaction UUID) alongside your order data. This makes it easy to reverse the correct transactions when processing returns. ### Scan Voucher Directly Instead of identifying the customer first and then retrieving their vouchers, customers can also scan a voucher QR code directly. This is useful when customers receive vouchers via email or printed flyers. #### 1. Trigger Voucher Scan Similar to the main loyalty flow, a button triggers the voucher scanning flow. This could be a separate "VOUCHER" button next to the "REWARDS" button, or integrated into the same flow. This flow is typically used when a customer presents a voucher code (from an email, flyer, or printed coupon) without first identifying themselves via their loyalty card. **Try it:** Click the green `Voucher` button on the POS screen to start the voucher scanning flow. #### 2. Scan Voucher QR Code The customer presents a voucher QR code (from an email, flyer, or printed coupon). Use the [Find Voucher](https://docs.leat.com/v3/oauth/vouchers.md#find-voucher) endpoint to retrieve the voucher details using the scanned code. **Endpoints for this step** - [Find Voucher](https://docs.leat.com/v3/oauth/vouchers.md#find-voucher) **Try it:** Hover over the scan area and click to simulate scanning a voucher QR code. #### 3. Link Customer (If Not Linked) If the voucher is not yet linked to a contact, prompt the customer to scan their loyalty card to link the voucher to their account. This use case is relatively rare, as most vouchers are linked to a contact upon creation. **Endpoints for this step** - [Find Contact Identifier](https://docs.leat.com/v3/oauth/contact-identifiers.md#find-contact-identifier) If the voucher is already linked to a contact, this step can be skipped. This process can also be used to link the contact to the order itself, so the customer can earn points. **Note:** When redeeming an unlinked voucher, you'll need to pass the `contact_uuid` parameter to the Redeem Voucher API call to link the transaction to the customer. #### 4. Confirm & Redeem Voucher Show the voucher details and ask the customer to confirm redemption. The Promotion linked to the Voucher should contain custom attributes (like `discount_rule_id`) that enable the POS to apply the discount. **Endpoints for this step** - [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) - [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) Use [Lock Voucher](https://docs.leat.com/v3/oauth/vouchers.md#lock-voucher) to reserve it, then [Redeem Voucher](https://docs.leat.com/v3/oauth/vouchers.md#redeem-voucher) after successful payment. **Note:** Applying discount logic to the cart is often the most complex part of the integration. See the [Custom Attributes](#custom-attributes) section for how to configure discount logic. ### Buying Giftcards This flow covers selling physical giftcards at the POS. These are pre-printed cards (already registered in the system with a €0 balance) that the cashier grabs from behind the counter when a customer wants to purchase one. Digital giftcards can also be purchased, though this use case is less common for POS systems. #### 1. Trigger Giftcard Sale When a customer wants to buy a giftcard, the cashier clicks the "GIFTCARD" button to start the flow. The cashier then grabs a physical giftcard from behind the counter. **Try it:** Click the `Giftcard` button on the POS screen to start the giftcard sale flow. #### 2. Scan Giftcard The cashier scans the QR code on the physical giftcard. The system calls the [Find Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#find-giftcard) endpoint to retrieve the giftcard details and verify it has a €0 balance (i.e., it hasn't been sold yet). **Endpoints for this step** - [Find Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#find-giftcard) #### 3. Choose Amount The customer chooses how much credit to load onto the giftcard. Common options are preset amounts (€10, €25, €50, etc.) but a custom amount can also be entered. #### 4. Giftcard Added to Cart The giftcard appears as a line item in the cart for the chosen amount. The customer pays for the giftcard like any other product. **Endpoints for this step** - [Create Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#create-giftcard-transaction) After successful payment, the system calls [Create Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#create-giftcard-transaction) with a **positive amount** to load the credit onto the giftcard. For digital giftcards (not physical cards), the flow is slightly different: 1. [Create Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#create-giftcard) — creates a new giftcard in the system 2. [Create Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#create-giftcard-transaction) — loads the purchased amount 3. [(Optional) Send Giftcard by Email](https://docs.leat.com/v3/oauth/giftcards.md#send-giftcard-by-email) — sends the giftcard code to the recipient ### Paying with Giftcards Customers can use their giftcard balance to pay for (part of) their order. This flow is triggered at the payment step. #### 1. Select Giftcard Payment At the payment step, the customer indicates they want to pay with a giftcard. The cashier selects the "GIFTCARD" payment method. **Try it:** Click the `Giftcard` payment button to start the giftcard payment flow. #### 2. Scan Giftcard The customer scans or enters their giftcard code (e.g., 'HT123ABC'). The system calls [Find Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#find-giftcard) to retrieve the current balance. **Endpoints for this step** - [Find Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#find-giftcard) **Try it:** Hover over the scan area and click to simulate scanning a giftcard. #### 3. Confirm Payment The system shows the giftcard balance and calculates how much will be deducted. The customer confirms they want to use their giftcard funds. **Endpoints for this step** - [Find Giftcard](https://docs.leat.com/v3/oauth/giftcards.md#find-giftcard) - [Create Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#create-giftcard-transaction) Upon confirmation, the system calls [Create Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#create-giftcard-transaction) with a **negative amount** to deduct from the balance. **Note:** If the giftcard balance doesn't cover the full order, the remaining amount can be paid with another payment method (card, cash, etc.). If the order is cancelled or fails after the giftcard transaction has been created, use the [Reverse Giftcard Transaction](https://docs.leat.com/v3/oauth/giftcard-transactions.md#reverse-giftcard-transaction) endpoint to restore the giftcard's balance. ### Buying Prepaid Credit Prepaid credit is tied to a customer's account, not a physical card. Customers can top up their prepaid balance at the POS and use it for future purchases. #### 1. Trigger Prepaid Top-up When a customer wants to add credit to their prepaid balance, the cashier clicks the "PREPAID" button to start the flow. **Try it:** Click the `Prepaid` button on the POS screen to start the prepaid top-up flow. #### 2. Identify Customer Since prepaid credit is tied to a customer account (not a physical card), the customer must first be identified by scanning their loyalty card or entering their details. **Endpoints for this step** - [Find Contact Identifier](https://docs.leat.com/v3/oauth/contact-identifiers.md#find-contact-identifier) The system calls [Find Contact Identifier](https://docs.leat.com/v3/oauth/contact-identifiers.md#find-contact-identifier) to retrieve the customer and their current prepaid balance. #### 3. Choose Amount The customer chooses how much credit to add to their prepaid balance. The screen shows their current balance and preset amount options. **Endpoints for this step** - [Get Prepaid Balance](https://docs.leat.com/v3/oauth/contacts.md#get-prepaid-balance) #### 4. Top-up Added to Cart The prepaid top-up appears as a line item in the cart. The customer pays for it like any other product. **Endpoints for this step** - [Create Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#create-transaction) After successful payment, the system calls [Create Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#create-prepaid-transaction) with a **positive amount** to add credit to the customer's prepaid balance. ### Paying with Prepaid Credit Customers who have prepaid credit on their account can use it to pay for (part of) their order. Since prepaid is tied to their account, the customer must already be identified. #### 1. Select Prepaid Payment At the payment step, if the customer has been identified and has prepaid credit, the "PREPAID" payment option becomes available. The cashier selects it to use the customer's balance. **Try it:** Click the `Prepaid` payment button to start the prepaid payment flow. #### 2. Scan Identifier If the customer hasn't identified themselves yet at this point, they must do so before proceeding with the prepaid payment. **Endpoints for this step** - [Find Contact Identifier](https://docs.leat.com/v3/oauth/contact-identifiers.md#find-contact-identifier) #### 3. Confirm Payment The system shows the customer's prepaid balance and calculates how much will be deducted. The customer confirms they want to use their prepaid credit. **Endpoints for this step** - [Get Prepaid Balance](https://docs.leat.com/v3/oauth/contacts.md#get-prepaid-balance) - [Create Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#create-transaction) Upon confirmation, the system calls [Create Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#create-prepaid-transaction) with a **negative amount** to deduct from the balance. **Note:** If the prepaid balance doesn't cover the full order, the remaining amount can be paid with another payment method (card, cash, etc.). If the order is cancelled or fails after the prepaid transaction has been created, use the [Reverse Prepaid Transaction](https://docs.leat.com/v3/oauth/prepaid-transactions.md#reverse-transaction) endpoint to restore the customer's balance. Source: https://docs.leat.com/v3/tutorials/pos