# eldo wallet Developers > eldo wallet API v2: create, send and update Apple Wallet and Google Wallet passes (loyalty cards, coupons, event, transit and hotel passes). ## Guides ### Apple Wallet and Google Wallet API guides Source: https://developers.eldowallet.fr/en/docs/ # Apple Wallet and Google Wallet API guides The eldo wallet API creates **Apple Wallet** and **Google Wallet** passes and keeps them up to date. The most common case: your **loyalty card**, designed once then sent to each of your customers, with up-to-date points. The same logic applies to coupons, event tickets, transit tickets and hotel bookings: you design a **template** once, then issue one **pass** per customer. ## At a glance | | | |---|---| | Base URL | `https://api-v2.test.eldowallet.fr` (test, default) — [environments](/docs/environments) | | Authentication | `Authorization: Bearer ` — [details](/docs/authentication) | | Format | JSON; `multipart/form-data` for endpoints that accept images — [details](/docs/files-and-images) | | Response language | `x-lang: fr` or `en` — [details](/docs/localization) | | Errors | A single envelope `{message, errorCode, path, date}` — [list of codes](/docs/errors) | | Pagination | `?page=1&limit=10` — [details](/docs/pagination) | | Rate limit | 1,000 requests per minute | | OpenAPI specification | [`openapi.en.yaml`](pathname:///openapi.en.yaml) · [`openapi.fr.yaml`](pathname:///openapi.fr.yaml) | ## API objects ```mermaid flowchart LR W[Wallet
your workspace] --> T[Templates
coupon, event,
transit, hotel, card] T --> P[Passes
one per customer] W --> C[Customers] C --> P P --> D[📱 Apple Wallet
Google Wallet] W --> N[Communications
& push notifications] N --> D ``` - **Wallet**: your eldo wallet workspace. It holds your branding, your sending settings and your access tokens. - **Template**: the design and rules of a pass type (colors, image, barcode, security). Example: the "Café Lumière" loyalty card, or a "Marseille – Ajaccio crossing" ticket template. - **Pass**: the copy issued to one person, with their data (name, seat, date…). It installs on the phone and updates remotely. - **Customer**: a holder of the wallet's store card. :::note[API v1] API v1 (`api.eldowallet.fr/api/v1`) will no longer be supported after **December 31, 2026**. See the [migration guide](/docs/migration-v1). ::: ### Authentication Source: https://developers.eldowallet.fr/en/docs/authentication # Authentication Every call (except login) must carry a token. There are two kinds of tokens, and you use them the same way. | | Login token (JWT) | Wallet token | |---|---|---| | Obtained with | | | | Tied to | a user (and their role) | your wallet | | Expires | yes (see below) | never, unless you set `expiresAt` | | Revoke by | changing the password | | | Use it for | testing, one-off scripts | **server integrations** | Tokens are specific to each [environment](/docs/environments): a test token doesn't work in production. ## Send the token ```http Authorization: Bearer ``` The API also accepts the `x-access-token: ` header and the `?access_token=` query parameter. If several are present, the order of precedence is `access_token`, then `x-access-token`, then `Authorization`. :::warning[Avoid `?access_token=`] A token in the URL ends up in proxy and server logs. Use the `Authorization` header. ::: ## Login token The response contains the token: `{"token": "eyJhbGciOi..."}`. - The login token **expires**. There is **no refresh token**: once the token has expired, the API returns `401:UNAUTHORIZED` and you log in again. - The token contains your role and the pass types enabled for your wallet. A call to a type that isn't enabled (for example `/transits` without the Transit option) returns `403:FORBIDDEN`. For a server integration, don't rely on the login token's expiry: use a wallet token. ## Wallet tokens For a server integration, create a dedicated token, store it in your secrets vault, and rotate it regularly. ```json { "_id": "6650f1c2a9e4b5d1c3a7e921", "token": "3f9c…e1a7", "tokenable": "6650a0b1c2d3e4f5a6b7c8d9", "tokenableType": "WALLET", "expiresAt": "2027-06-30T00:00:00.000Z" } ``` - Without `expiresAt`, the token never expires. - List your tokens with and revoke one with . ## Authentication errors | Code | When | |---|---| | `401:UNAUTHORIZED` | Missing, invalid or expired token, or wrong password | | `403:FORBIDDEN` | Valid token, but insufficient role or pass type not enabled | See all [errors](/docs/errors). ### Bulk operations Source: https://developers.eldowallet.fr/en/docs/bulk-operations # Bulk operations To import a whole customer base, issue hundreds of tickets or update thousands of cards, use the `/bulk` endpoints instead of looping over single calls. | Resource | Create | Update | Delete | |---|---|---|---| | Customers | | | | | Transit passes | | | | | Event passes | | | | | Hotel passes | | | | | Sent coupons | | | | ## Request body | Field | Type | Description | |---|---|---| | `items` | array | 1 to 5,000 items, each in the format of the single-item endpoint. Deletions take `ids` (an array of identifiers). | | `webhookUrl` | string | Optional. URL called once the operation is complete. | | `forceSync` | boolean | Optional, `false` by default. `true` processes the request immediately and returns the results. Only for batches of **fewer than 1,000** items (otherwise `400:SYNC_EXECUTION_NOT_ALLOWED`). | ## Asynchronous mode (default) The response comes back immediately, before processing: ```json { "operationId": "6651a7e0c4b2f19d8e3a0c55", "totalItems": 2, "successCount": 0, "failureCount": 0, "status": "pending", "createdAt": "2026-11-20T10:00:00.000Z" } ``` Track progress with : `status` moves from `pending` to `processing`, then `completed` or `failed`. At the end, every failed item is listed in `operationErrors`. ```mermaid sequenceDiagram participant S as Your server participant E as eldo wallet API S->>E: POST /clients/bulk E-->>S: 201 {operationId, status: "pending"} loop Every 5 to 10 s, or wait for the webhook S->>E: GET /bulk-operations/{operationId} E-->>S: {status: "processing", successCount: 1200} end E->>S: POST webhookUrl (operation complete) ``` :::warning[The completion webhook isn't guaranteed] `webhookUrl` is called **only once**, with no retry, and only if the operation ends as `completed`. Keep polling as a safety net. ::: ## Best practices - Split anything above 5,000 items, and send the batches one after another. - Provide your own `barcode` per item if you need to link the pass to your system (ticketing, CRM). - An error on one item doesn't cancel the others: retry only the items in `operationErrors`. Hundreds of thousands of cardholders, large send-outs or a move from another provider: our team sizes the plan with you and supports you. ### Changelog Source: https://developers.eldowallet.fr/en/docs/changelog # Changelog **Breaking** changes are marked with 🔴. ## October 2026 - **New developer space.** API reference generated from the OpenAPI specification, examples in eight languages, a "Try it" button, a single documented error format, use cases and a knowledge base, in French and English. - OpenAPI specification published: [`openapi.en.yaml`](pathname:///openapi.en.yaml), [`openapi.fr.yaml`](pathname:///openapi.fr.yaml). - 🔴 **End of support for API v1 on December 31, 2026.** See the [migration guide](/docs/migration-v1). ### Environments Source: https://developers.eldowallet.fr/en/docs/environments # Environments | Environment | Use | Base URL | |---|---|---| | **Test** | Default, to develop and test your integration | `https://api-v2.test.eldowallet.fr` | | Production | Going live, real customers | `https://api-v2.eldowallet.fr` | Both environments expose the same API. Every example in this documentation, the "Try it" button of the [API reference](/api) and the SDK target the test environment. The API has no prefix and no version segment: `https://api-v2.test.eldowallet.fr/clients`, not `/api/v2/clients`. **Tokens are specific to each environment**: a test token is rejected in production, and vice versa. ## Ready for production? Is your integration working in test? The next step happens with our team: they open your production wallet, walk you through the plan that fits your volumes and use cases, and support you until launch. Full access to the production API, pricing based on your volumes, launch support: tell us about your project and we'll get back to you. ## Before going live Your launch checklist, to review with our team when your access is opened: - **Use dummy data** in test (`@example.com`, fake phone numbers). Passes sent by email or SMS are actually delivered. - **Respect the rate limit**: 1,000 requests per minute. Beyond a few hundred sends, use [bulk operations](/docs/bulk-operations). - **Monitor the API's availability** from your own monitoring. - **Set up [webhooks](/docs/webhooks)** to track installations. ## Switching to production Once your production access is open: 1. Switch the base URL to `https://api-v2.eldowallet.fr`. 2. Use a token from your production wallet, created with and stored outside your code. Keep the base URL and the token in configuration (environment variables), never hard-coded. ### Errors Source: https://developers.eldowallet.fr/en/docs/errors # Errors All errors share the **same JSON envelope**, whatever the endpoint. ```json { "message": "Promo code already exists", "errorCode": "400:PROMO_CODE_ALREADY_EXISTS", "path": "/coupons", "date": "2026-01-15T09:30:00.000Z" } ``` | Field | Type | Description | |---|---|---| | `message` | string | Human-readable message, translated according to [`x-lang`](/docs/localization). Display it, **don't test it**. | | `errorCode` | string | `:`. **Stable**: this is the field your code should test. | | `path` | string | The URL called, including the query string. | | `date` | string | ISO 8601 timestamp. | | `details` | object | Optional. Values that help with diagnosis (e.g. remaining credits). | | `validationErrors` | array | Only present for `400:VALIDATION_ERROR`: `[{field, message}]`. | ## Validation errors ```json { "message": "email must be an email", "errorCode": "400:VALIDATION_ERROR", "path": "/clients", "date": "2026-01-15T09:30:00.000Z", "validationErrors": [ {"field": "email", "message": "email must be an email"}, {"field": "lastName", "message": "lastName must be longer than or equal to 2 characters"} ] } ``` `message` repeats the first error. For nested objects, `field` holds the name of the leaf field (`backgroundColor`), not the full path. ## Handle errors properly ```js const res = await fetch(url, options); if (!res.ok) { const err = await res.json(); const [, code] = err.errorCode.split(':'); switch (code) { case 'UNAUTHORIZED': // token expired: log in again break; case 'VALIDATION_ERROR': // fix the fields listed in err.validationErrors break; case 'PASS_BUILDER_ERROR': // temporary pass generation error: retry later break; default: throw new Error(`${err.errorCode}: ${err.message}`); } } ``` - **4xx**: the request is at fault. Don't resend it as is. - **5xx**: an error on eldo wallet's side. Retry with an increasing delay (1 s, 2 s, 4 s…). - Each page of the [API reference](/api) lists the errors specific to that endpoint, with a sample response. ## All codes ### Images and multipart Source: https://developers.eldowallet.fr/en/docs/files-and-images # Images and `multipart/form-data` Endpoints that accept an image (creating a coupon, event, transit, hotel or communication template, updating the wallet…) expect a **`multipart/form-data`** body. Each endpoint's reference page states the expected type. :::danger[A file can't travel inside JSON] Sending `{"strip": ""}` as `application/json` doesn't work. Use `multipart/form-data`, and let your HTTP client generate the `Content-Type` header (it contains the *boundary*). ::: ## The three multipart rules 1. **Files** are file fields: `-F "strip=@strip.png;type=image/png"`. 2. **Objects and arrays** (`colors`, `security`, `locations`, `displayOptions`, `iosHeader`, `date`…) are sent as **JSON strings**: `-F 'colors={"backgroundColor":"#131313","labelColor":"#9EE00D","foregroundColor":"#FFFFFF"}'` 3. **Booleans** are sent as the text `"true"` or `"false"`. Any other value counts as `false`. ## File constraints | | | |---|---| | Image formats | PNG or JPEG | | Image size | 1 KB to 5 MB | | CSV import | `text/csv`, 10 MB and 100,000 rows maximum | | Per request | 10 files and 50 MB per file maximum | An image outside these limits returns `400:VALIDATION_ERROR`. A malformed multipart body returns `400:INVALID_MULTIPART`, and an unsupported `Content-Type` returns `415:UNSUPPORTED_MEDIA_TYPE`. ## Updates Most `PATCH` endpoints accept **multipart or JSON**. Use JSON when you aren't changing an image: it's simpler. For recommended pass image dimensions, see the [knowledge base](/kb). ### Response language Source: https://developers.eldowallet.fr/en/docs/localization # Response language The API responds in **English** by default, or in **French**. The language applies to error messages and to content generated by eldo wallet. The language is read in this order: 1. the `?lang=en` query parameter 2. the `x-lang: en` header 3. the `Accept-Language` header ```json {"message": "Model not found!", "errorCode": "404:MODEL_NOT_FOUND", "path": "/clients/unknown?lang=en", "date": "…"} ``` :::note A few validation messages are always in English, even when you request French (for example `departureDate must be a valid ISO date`). Always test `errorCode`, never `message`. ::: ## Your pass content The API language doesn't translate your passes: the text you send (`details`, `terms`, event name…) is displayed as is. For a multilingual audience, create one template per language, or adapt the pass fields for each customer. ### Migrating from API v1 Source: https://developers.eldowallet.fr/en/docs/migration-v1 # Migrating from API v1 :::danger[End of support for API v1: December 31, 2026] API v1 (`https://api.eldowallet.fr/api/v1`) will no longer be supported after **December 31, 2026**. New features are only available in v2. **Our support team will help you migrate**: [contact us](#support) to plan your move to v2. ::: ## Who is affected? Any integration that calls a URL starting with `https://api.eldowallet.fr/api/v1/`. If you only use the eldo wallet back office, you have nothing to do. ## What changes | | v1 | v2 | |---|---|---| | Base URL | `https://api.eldowallet.fr/api/v1` | `https://api-v2.eldowallet.fr` (production); to try it: `https://api-v2.test.eldowallet.fr` — [environments](/docs/environments) | | Wallet ID | in the URL (`/wallets/{wallet}/transits`) | derived from the token (`/transits`) | | Login | `POST /users/login` | | | Server tokens | — | revocable [wallet tokens](/docs/authentication#wallet-tokens) | | Error format | `{statusCode, status, messageCode, message}` | `{message, errorCode, path, date}` — [details](/docs/errors) | | Pagination | varies by endpoint | uniform `?page=&limit=` — [details](/docs/pagination) | | Bulk sending | — | up to 5,000 items, with progress tracking — [details](/docs/bulk-operations) | | Webhooks | plain URL | typed events, retries — [details](/docs/webhooks) | ## Main endpoint mapping | Need | v1 | v2 | |---|---|---| | Create a transit template | `POST /wallets/{wallet}/transits` | | | Send a transit ticket | `POST /wallets/{wallet}/transits/{transit}/send` | | | Update a trip | `PATCH /wallets/{wallet}/transits/{transit}/trip/{tripNumber}` | | | Notify a trip | `POST /wallets/{wallet}/transits/{transit}/trip/{tripNumber}` | | | Create a coupon | `POST /wallets/{wallet}/coupons` | | | Send a coupon | `POST /wallets/{wallet}/coupons/{coupon}/send` | | | Create an event | `POST /wallets/{wallet}/events` | | | Create a customer | `POST /customers` | | | Redeem a pass | `PUT /wallets/{wallet}/redeem` | | | Create a hotel pass | `POST /wallets/{wallet}/hotels` | | In v2, boarding passes are issued as transit passes (`type: "Air"`, see [Airlines](/use-cases/airlines)) and movie tickets as events (see [Cinema](/use-cases/cinema)). ## Migration steps Log in to v2 and create a wallet token dedicated to your integration. List your templates in v2 to get their IDs, and create the ones your integration needs. Change the base URL, remove the wallet ID from paths, and adapt request bodies following the [API reference](/api). Test `errorCode` (e.g. `400:VALIDATION_ERROR`) instead of `statusCode` / `messageCode`. Switch over gradually, monitor errors, then turn off v1 calls before December 31, 2026. The legacy v1 documentation remains available at [api.eldowallet.fr/api-docs](https://api.eldowallet.fr/api-docs) until the end of support. ### Pagination Source: https://developers.eldowallet.fr/en/docs/pagination # Pagination List endpoints (`GET /clients`, `GET /transits`, `GET /events/{id}/passes`…) are paginated. | Parameter | Default | Constraints | |---|---|---| | `page` | `1` | integer ≥ 1 | | `limit` | `10` | integer from 1 to 1,000 | ```json { "docs": [{"_id": "…", "firstName": "Camille", "lastName": "Martin"}], "totalDocs": 1342, "limit": 100, "page": 2, "totalPages": 14, "pagingCounter": 101, "hasPrevPage": true, "hasNextPage": true, "prevPage": 1, "nextPage": 3 } ``` ## Iterate over a whole list ```js title="Iterate over all pages" pattern async function* allClients(token) { let page = 1; while (page) { const res = await fetch(`https://api-v2.test.eldowallet.fr/clients?page=${page}&limit=500`, { headers: {Authorization: `Bearer ${token}`}, }); const body = await res.json(); yield* body.docs; page = body.nextPage; // null on the last page } } ``` :::tip[For a full export] To export all your customers, use the background CSV export instead: . ::: ### Create an Apple Wallet and Google Wallet loyalty card in 10 minutes Source: https://developers.eldowallet.fr/en/docs/quickstart # Create an Apple Wallet and Google Wallet loyalty card in 10 minutes The loyalty card is eldo wallet's most used pass. In three calls, you'll design it, create a customer and email them their card. Allow 10 minutes. **Prerequisites**: an eldo wallet account on the [test environment](/docs/environments) (`https://api-v2.test.eldowallet.fr`), which every example below uses. Every example is available in cURL, Node.js, PHP, Python, Java, C#, Go, Ruby and with the Node.js SDK: pick your language once and it's remembered across the site. The **Try it** button opens the endpoint page, where you can call it with your token. ## 0. (Optional) Install the Node.js SDK Using Node.js? Generate a typed client, with autocompletion on every endpoint and field, then use the **SDK Node.js** tab of the examples below. Method names are the operation ids shown in the [API reference](/api). Other languages: [SDKs and tools](/docs/sdks). ## 1. Get a token Response: ```json {"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."} ``` Keep this token: it replaces `` in the next examples. ```bash export ELDO_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." ``` :::tip[On a server, use a wallet token] A login token is tied to a user. For a server integration, create a dedicated [wallet token](/docs/authentication#wallet-tokens). ::: ## 2. Design your loyalty card The loyalty card is unique per wallet: you set its design once, and each customer gets their own copy. Choose the type (`STORE_CARD` for a store card with a balance, `MEMBER_CARD` for a membership card), the program name, the colors, the balance unit and the fields shown on the card. This endpoint receives an image (`strip`, the card's banner): the body is **`multipart/form-data`**, and objects (`colors`, `descriptions`) are sent as a JSON string. The logo, icon and contact details shown on the card are set on the wallet (). Design tips: [design guidelines](/kb/concepts/design-guidelines). ## 3. Create a customer and send their card This time the body is JSON. eldo wallet generates the customer's card; with `sendCard: true`, it emails them the install link. The response contains the customer (`_id`, `barcode`) and their card's install link (`shortLink`), which you can also share in your own channels. Camille opens the link and the card is added to Apple Wallet or Google Wallet. ```bash export CLIENT_ID="6650f1c2a9e4b5d1c3a7e921" ``` ## 4. (Optional) Update their points After a purchase, send the customer's **new balance**: the value replaces the previous balance (it isn't added to it), and each value is kept in the history. The card updates on their phone. From a checkout that scans the card, use the barcode instead: . ## Next steps - The full flow (coupons, tiers, stores): [Retail use case](/use-cases/retail). - Segment your members (Silver, Gold): [customer types](/kb/concepts/customer-types). - Import your existing customers: [CSV import](/kb/guides/import-customers-csv) or [bulk operations](/docs/bulk-operations). - Bring the card up near your stores: [locations](/kb/concepts/locations). - Get notified when a card is installed: [webhooks](/docs/webhooks). - Ready to go live: [environments](/docs/environments). Our team opens your production access, walks you through the plans and helps you launch your programme to your customers. ### SDKs and tools Source: https://developers.eldowallet.fr/en/docs/sdks # SDKs and tools Everything starts from the **OpenAPI specification**, published with this documentation and updated whenever the API changes: - [`https://developers.eldowallet.fr/openapi.en.yaml`](pathname:///openapi.en.yaml) (English descriptions) - [`https://developers.eldowallet.fr/openapi.fr.yaml`](pathname:///openapi.fr.yaml) (French descriptions) ## Node.js: a ready-to-use client with `api` ```bash npx api install https://developers.eldowallet.fr/openapi.en.yaml --identifier eldowallet ``` The tool generates a typed SDK in your project and prints the package name to import: ```js import eldowallet from '@api/eldowallet'; eldowallet.auth(process.env.ELDO_TOKEN); const {data} = await eldowallet.getClients({page: 1, limit: 20}); ``` Method names are the operation IDs shown on each page of the [API reference](/api). ## TypeScript: types only ```bash npx openapi-typescript https://developers.eldowallet.fr/openapi.en.yaml -o src/eldowallet.d.ts ``` ## Other languages (PHP, Python, Java, C#…) [OpenAPI Generator](https://openapi-generator.tech) produces a client in more than 50 languages: ```bash npx @openapitools/openapi-generator-cli generate \ -i https://developers.eldowallet.fr/openapi.en.yaml \ -g php -o ./eldowallet-php --additional-properties=invokerPackage=EldoWallet ``` ```bash npx @openapitools/openapi-generator-cli generate \ -i https://developers.eldowallet.fr/openapi.en.yaml \ -g python -o ./eldowallet-python --additional-properties=packageName=eldowallet ``` ```bash npx @openapitools/openapi-generator-cli generate \ -i https://developers.eldowallet.fr/openapi.en.yaml \ -g java -o ./eldowallet-java ``` ```bash npx @openapitools/openapi-generator-cli generate \ -i https://developers.eldowallet.fr/openapi.en.yaml \ -g csharp -o ./EldoWallet ``` ## Postman and Insomnia A ready-to-use collection: all 129 API requests, grouped by domain, already set up for the test environment and authentication. Download the Postman collection 1. In Postman, click **Import** and drop the downloaded file. 2. Open the **eldo wallet API v2 (test)** collection, **Variables** tab, and paste your API token into `token`. No token yet? [Request test access](/docs): it arrives by email. 3. Send any request: the test URL and the `Authorization: Bearer` header are already set. **Insomnia, Bruno or another tool:** import the OpenAPI specification from its URL. ```text title="Specification URL" https://developers.eldowallet.fr/openapi.en.yaml ``` ## AI assistants - [`/llms.txt`](pathname:///llms.txt): an index of all the documentation, in [llmstxt.org](https://llmstxt.org) format. - [`/llms-full.txt`](pathname:///llms-full.txt): all the documentation in a single text file. - Every page is static HTML: an assistant can read any URL on this site. Give one of these URLs to your coding assistant (Claude, Copilot, Cursor…) so it writes the integration from the real specification. ### Webhooks Source: https://developers.eldowallet.fr/en/docs/webhooks # Webhooks eldo wallet can call your server when an event happens in your wallet. ## Events | Event | Trigger | |---|---| | `client.created` | Customer created (API, bulk operation, public sign-up form, CSV import) | | `client.updated` | Customer updated, including their points (`gain`) | | `client.deleted` | Customer deleted | | `pass.installed` | A phone added the pass to Apple Wallet or Google Wallet | | `pass.uninstalled` | A phone removed the pass | ## Configure the URL Set `webHookUrl` on your wallet with . Separate multiple URLs with commas. ### Authenticate the calls Webhooks **aren't signed**. To check that a call comes from eldo wallet, add your own headers with the `#headers=` fragment: `Name:Value`, comma-separated, with URL-encoded values. eldo wallet sends them with every call, and never shows them again when you read the wallet back. Check this header on your server and reject calls that don't carry it. ## Format `POST`, `Content-Type: application/json`: ```json { "event": "pass.installed", "walletId": "6650a0b1c2d3e4f5a6b7c8d9", "timestamp": "2026-11-20T10:02:13.408Z", "data": { "pass": {"_id": "6651b2…", "kind": "TRANSIT", "status": "active", "barcode": "MRS-AJA-000123", "activeIOSDeviceCount": 1, "activeAndroidDeviceCount": 0}, "barcode": "MRS-AJA-000123", "deviceType": "IOS" } } ``` For `client.*`, `data.client` contains the full customer (`_id`, `firstName`, `lastName`, `email`, `phoneNumber`, `barcode`, `source`, `gain`…). ## Delivery and retries - Respond with a **2xx within 10 seconds**. Do any heavy processing after responding. - On failure, eldo wallet retries **3 times** with an increasing delay (starting at 2 s). - The same event can arrive more than once, and order isn't guaranteed: make your handler **idempotent** (for example on `event` + `data.pass._id` + `timestamp`). ```js // Express app.post('/hooks/eldo', express.json(), (req, res) => { if (req.get('Authorization') !== `Bearer ${process.env.ELDO_WEBHOOK_SECRET}`) return res.sendStatus(401); res.sendStatus(204); // respond right away queue.push(req.body); // process later }); ``` ## Bulk operation completion webhook The `/bulk` endpoints accept a per-request `webhookUrl`, called only once at the end. See [bulk operations](/docs/bulk-operations). ## Use cases ### Apple Wallet and Google Wallet use cases by industry Source: https://developers.eldowallet.fr/en/use-cases/ # Apple Wallet and Google Wallet use cases A loyalty card, a train ticket, a boarding pass or a hotel booking: the mechanics never change. You create a **template** once, you **send** a pass to each customer, then you **keep it alive** (updates, notifications, redemption). Each page below starts from a real industry. It walks through the customer journey, the API calls in order and the traps to avoid. Pick yours, or the closest one. ## How to read a use case Every page follows the same structure: 1. **The pitch**: the business problem and what the pass does on the customer's phone. 2. **The customer journey**: a diagram showing who calls whom, from your system to the customer's wallet. 3. **The integration workflow**: numbered steps. Each step shows who acts and which endpoints are called. | Badge | Who acts | | --- | --- | | **Back-office** | You, in the eldo wallet back-office. No code. | | **Your server** | Your CRM, POS, ticketing system or back end calls the API. | | **eldo wallet** | The platform generates, sends or updates the passes. | | **End customer** | Your customer adds, shows or receives the pass. | 4. **Best practices**: what works in production, and what breaks. 5. **Go further**: related knowledge base articles and endpoints. :::tip[New to eldo wallet?] Start with the [quickstart](/docs/quickstart) and [authentication](/docs/authentication). Then read [templates and passes](/kb/concepts/templates-vs-passes): it is the idea the whole API is built on. ::: Every project is different: describe yours and we'll show you how wallet cards can fit. ### Apple Wallet and Google Wallet boarding passes Source: https://developers.eldowallet.fr/en/use-cases/airlines At the airport, passengers look for three things: **their gate, their boarding time, their seat.** A boarding pass in Apple Wallet or Google Wallet shows them at a glance, from the lock screen, with no app and no network. You issue the pass at check-in, update it if the gate or time changes, and the passenger shows it at security and at the gate. ## Customer journey ```mermaid sequenceDiagram autonumber actor P as Passenger participant D as Booking system participant E as eldo wallet participant W as Apple / Google Wallet D->>E: Creates the CDG - JFK route, type Air P->>D: Checks in online D->>E: Issues the pass with flight, seat and gate E-->>P: Email with the add link P->>W: Adds the boarding pass Note over D,E: 40 min delay D->>E: New time for today's flight E->>W: Passes updated P->>D: Scan at security and at the gate D->>E: Redeems the pass ``` ## Integration workflow Boarding passes use transits with the `Air` type. One template per route: origin and destination airports (IATA codes and names), reference times, terms, customer service number, colors, strip. `PDF_417` is the standard boarding pass barcode. On your website, app or at the counter. One call per passenger: identity, email, flight date, `flightNumber`, `gate`, `seat`, barcode. Use one `tripNumber` per flight and date (for example `EW204-20261112`): that's what lets you update every passenger at once. It works offline and shows up on the lock screen on the day of the flight. Delay: update the times for the whole flight by its `tripNumber`. Gate change: the gate is stored on each pass, so update the passes in bulk. Add a message to explain: "Gate K42 changed to L12". The reader scans the barcode. Look up the pass, then redeem it to record boarding. ## Best practices - **One `tripNumber` per flight and date.** EW 204 on the 12th and EW 204 on the 13th are two different flights. - **Use IATA codes.** Station codes are shown large on the pass. Full names go in secondary fields. - **Check barcode readability.** Test your gate readers with a phone on auto-brightness before launch. - **Connections.** Issue one pass per segment, each with its own `tripNumber`. ## Go further - [Transport & mobility](/use-cases/transport) - [Update a trip](/kb/guides/update-a-trip) - [Barcodes](/kb/concepts/barcodes) - [Apple Wallet or Google Wallet](/kb/concepts/apple-vs-google-wallet) - Endpoints: ## Get inspired A boarding pass can do much more than get passengers through the gate. Here are ideas to enrich your passengers’ journey. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Membership cards for associations in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/associations An association lives through its members. Every year you chase renewals, print cards, post them, announce the general meeting. **A member card in Apple Wallet or Google Wallet does it all at once:** proof of membership, a direct information channel and a ticket for your events. You import your member list once, then keep it up to date. ## Customer journey ```mermaid sequenceDiagram autonumber actor A as Member participant G as Membership software participant E as eldo wallet participant W as Apple / Google Wallet G->>E: Imports the member list E-->>A: Email with the card A->>W: Adds the member card Note over G,E: General meeting G->>E: Message to all members W-->>A: General meeting on 14 March at 6 pm, village hall G->>E: Issues the annual party ticket A->>W: Adds the ticket Note over G,E: End of the membership year G->>E: Renewal reminder A->>G: Pays the yearly fee G->>E: Updates the renewal date ``` ## Integration workflow Pick `MEMBER_CARD`, show the association (`ASSOCIATION`) and the member number (`N_ADHERENT`). On iPhone, show the category in the header (`iosHeader` set to `TYPE`). Keep the same card from one year to the next: only the category and the renewal date change. Add your contacts and website on the wallet. Active member, supporter, youth, volunteer, honorary member: each category becomes a client type. You then target your messages by category. See [Client types](/kb/concepts/customer-types). You already have a spreadsheet or an export from your membership software: import it as CSV. The import runs in the background. With `sendNotificationsToCustomers: true`, each member gets their card. For a regular sync, use bulk creation in JSON (up to 5,000 rows per call). See [Import a customer base](/kb/guides/import-customers-csv). When the fee comes in, your software creates the member with their number as the barcode, their category, admission date and renewal date. They show it at an activity or at a partner's, even offline. A week after the import, remind members whose card isn't installed: all of them, or a specific list. General meeting notice, call for volunteers, activity moved to a new venue: a notification to all members, or to one category (`target: SPECIFIC`). For a campaign (fundraiser, open day), create a communication: the card's strip changes for the period. See [Push notifications](/kb/push-notifications). Annual party, outing, tournament, talk: create an event, then issue a ticket to each person who signs up. Pass the member's `clientId` to link the ticket to their record. Outside guests get a ticket without being members. See [Events & trade fairs](/use-cases/events). At the door, scan the ticket and redeem it. A second scan returns `PASS_ALREADY_REDEEMED`. A month before the membership year ends, schedule a reminder to the members concerned. When the fee comes in, update the renewal date and, if needed, the category. The installed card updates. ## Best practices - **Member number as the barcode.** It's the number in your register: no mapping table. - **One card, many years.** On renewal, update the existing card rather than issuing a second one. - **Only what's needed on the card.** Name, number, category. Everything else stays in your membership software. - **Useful messages.** General meeting, activities, volunteering: `INFORMATIVE`. A partner's offer is `COMMERCIAL`. Respect each member's communication preferences. - **Clean the file before importing.** One unique email and member number per row: duplicates are rejected. ## Go further - [Events & trade fairs](/use-cases/events) for tickets - [Health insurance](/use-cases/health-insurance) for another member card - [Import a customer base](/kb/guides/import-customers-csv) - [Client types](/kb/concepts/customer-types) - [Push notifications](/kb/push-notifications) - Endpoints: ## Get inspired The member card can keep your association alive all year round. Here are ideas to keep your members engaged. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Garage loyalty cards in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/automotive In automotive, you engage two audiences. **The trade customer** (mechanic, body shop) wants a points program on parts orders. **The private customer** wants a nudge when the car is due for a service or a tyre change. eldo wallet handles both from the same wallet, with an Apple Wallet or Google Wallet card that shows the plate number and the next due date. ## Customer journey ```mermaid flowchart LR subgraph Pro["Trade network"] A[Sales rep or extranet] -->|creates the trade card| E((eldo wallet)) P[Parts order] -->|points credited| E E -->|tier reached| R[Reward coupon] end subgraph Part["Private customer"] G[Garage workshop] -->|card with plate number| E CRM[Workshop CRM] -->|service due in 30 days| E E -->|notification| T[Customer phone] end ``` ## Integration workflow Pick `MEMBER_CARD`, the `POINTS` gain unit and the displayed fields: the right-hand field can show the plate number (`N_IMMATRICULATION`), the left-hand one the gain. Create one client type per audience: "Trade", "Private". You then target each audience separately. See [Client types](/kb/concepts/customer-types). The sales rep visits a garage. A button in your CRM creates the client with the "Trade" type and `sendCard: true`. The owner gets the card within 30 seconds. Same flow from your trade extranet. Add the vehicle model, the next service date and a booking link to the back of the card (`additionalInformations`). A per-customer strip is possible too. Your ordering system sends the gain after each validated invoice. The balance updates on the trade customer's card. Brake campaign, new injector, winter tyres: create a communication with its visual and period. The cards' strip changes during the campaign. Target a points range (`minPoints`, `maxPoints`) to keep an offer for your best trade customers. Then notify the network. Your workshop CRM detects vehicles due for service and sends a `SPECIFIC` notification with a personal message per customer: "Your service is coming up, book a slot." 2,000-point tier: reward coupon. No order in 60 days: win-back coupon. The coupon is redeemed at the counter. Compare orders and average basket for card holders and non-holders in your ERP. ## Best practices - **One wallet, two client types.** Don't mix B2B and B2C messages: client types keep the two audiences apart. - **The service reminder is your best message.** It's useful, personal and expected: tag it `INFORMATIVE`. Keep promotions (`COMMERCIAL`) for trade customers. - **One communication at a time.** Two communications overlapping on the same period are rejected (`OVERLAPPING_COMMUNICATIONS`). - **QR codes on catalogs.** Print the back-office sign-up QR code on your catalogs and point-of-sale material. No development needed. ## Go further - [Client types](/kb/concepts/customer-types) - [Import a customer base](/kb/guides/import-customers-csv) - [Push notifications](/kb/push-notifications) - Endpoints: ## Get inspired For trade customers and drivers alike, the Wallet card can follow the whole life of the vehicle. Here are ideas to go further. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Cinema tickets in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/cinema The moviegoer buys a seat on the subway, arrives five minutes before the show and digs for the email. **With a ticket in Apple Wallet or Google Wallet, they open the lock screen and walk in.** Screen, row and seat are shown in large type. After the film, a snack coupon or an invitation to the next preview brings them back. ## Customer journey ```mermaid sequenceDiagram autonumber actor S as Moviegoer participant T as Cinema ticketing participant E as eldo wallet participant W as Apple / Google Wallet T->>E: Creates the 8:45 pm screening S->>T: Buys a seat online T->>E: Issues the ticket screen 7, row H, seat 12 E-->>S: Email with the link S->>W: Adds the ticket S->>T: Scan at the screen entrance T->>E: Redeems the ticket T->>E: Sends a snack coupon ``` ## Integration workflow A screening is an event: film in `name`, synopsis in `description`, start and end times, price, terms, cinema address and poster as the strip. The three seating labels become Screen (`ZONE` on the left), Row (`ROW` in the center) and Seat (`SEAT` on the right). Create it automatically from your programming. One call per seat sold, with screen, row and seat. A group booking? Issue in bulk. The ticket works offline and is ready to scan. Staff scan the ticket and the pass is redeemed. A mistaken scan is fixed by undoing the redemption. Create a "€2 off the popcorn combo" or "€6 seats on Tuesdays" coupon and send it after the show, even to a moviegoer who isn't a client yet (first name, last name, email). ## Best practices - **One screening, one event.** The same film at 6 pm and 8:45 pm is two events, each with its own time. Reuse the poster. - **Use the poster as the strip.** Moviegoers recognise their film at a glance. See [Design guidelines](/kb/concepts/design-guidelines). - **Turn on one-time use** to block a second entry with a screenshot. - **Declare the cinema's location** so the ticket shows up on arrival. See [Locations](/kb/concepts/locations). ## Go further - [Events & trade fairs](/use-cases/events) - [Retail & fashion](/use-cases/retail) for coupon mechanics - [Pass lifecycle](/kb/concepts/pass-lifecycle) - Endpoints: ## Get inspired A cinema ticket can bring moviegoers back much more often. Here are ideas to make it happen. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Event tickets in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/events A PDF ticket gets lost in the inbox. An Apple Wallet or Google Wallet ticket **shows up on its own on the lock screen on the day**, as the attendee nears the venue. You issue the ticket at purchase, update it if the date or venue changes, and redeem it at the door. Attendees have nothing to look for. ## Customer journey ```mermaid sequenceDiagram autonumber actor S as Attendee participant T as Ticketing participant E as eldo wallet participant W as Apple / Google Wallet T->>E: Creates the event with venue and times S->>T: Buys a ticket T->>E: Issues the ticket with seating E-->>S: Email with the add link S->>W: Adds the ticket Note over T,E: Venue change T->>E: Updates the event E->>W: Every ticket updated S->>T: Scan at the door T->>E: Redeems the ticket E-->>T: Second scan rejected ``` ## Integration workflow The event is the template shared by every ticket: name, description, start and end dates, door opening, price, terms, contact, website, colors and official strip. Add the venue location so the ticket shows up as attendees arrive. Finally, pick the three seating labels: left (`ZONE`, `ACCESS`…), center (`ROW`), right (`SEAT`, `PLACE`…). Your ticketing system calls eldo wallet for each ticket, with the attendee's identity and the three seating values. If the attendee is already a client of your wallet, also pass their `clientId`. Press, sponsor guests, corporate groups: up to 5,000 tickets in one request, processed asynchronously. See [Send to non-clients](/kb/guides/send-to-non-clients). The ticket works offline and survives a phone change. One call on the event updates every ticket issued: new date, new address, new visual. Just one attendee changing seats? Update their ticket. The scanner reads the QR code. Look up the ticket by barcode, show the seating, then redeem. A second scan returns `PASS_ALREADY_REDEEMED`. Scanned by mistake? Undo the redemption. List the event's tickets and their status to follow entries in real time. ## Best practices - **One event per session.** A ten-date tour is ten events. Reuse the same visual. - **Turn on one-time use and the rotating barcode** for high-value events. Security options limit resale and screenshots. - **Declare the venue location.** It's what puts the ticket on the lock screen. See [Locations](/kb/concepts/locations). - **Multipart: objects as JSON strings.** `date`, `locations`, `colors` and `security` go as JSON strings in the form. It's the most common mistake. - **Test the scanning flow on site** before doors open: throughput, brightness, network inside the venue. ## Go further - [Sport & clubs](/use-cases/sport) for match tickets - [Cinema](/use-cases/cinema) - [Pass lifecycle](/kb/concepts/pass-lifecycle) - Endpoints: ## Get inspired A Wallet ticket can stay with attendees before, during and after the event. Here are ideas to go further. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Ferry tickets in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/ferry A crossing is planned the day before and lived in the 30 minutes before boarding. eldo wallet steps into both moments. **The ticket is in Apple Wallet or Google Wallet from the moment of booking**, the reminder arrives at the right time, and so does the dock change. On a delay, passengers wait at home, not on the quay. ## Customer journey ```mermaid sequenceDiagram autonumber actor P as Passenger participant R as Booking system participant E as eldo wallet participant W as Apple / Google Wallet P->>R: Books Marseille - Ajaccio, 4 passengers, cabin R->>E: Issues the ticket with tripNumber and cabin E-->>P: Email with the Add button P->>W: Adds the ticket Note over R,E: Departure day, 30 min before R->>E: Message to the trip's holders W-->>P: Boarding at dock B6 R->>E: 1 h delay, new time E->>W: Tickets updated P->>R: Scan at boarding ``` ## Integration workflow Use the `Boat` type. Fill in the ports and their codes, reference times, fare, terms of carriage and customer service number. Set `seatName=N_CABINE` so the seat field shows as a cabin number. One template per route is enough. Your booking system calls eldo wallet for each ticket, with the sailing's `tripNumber` and the cabin in `seat`. Add the passenger list and the vehicle on board as custom back fields (`customBackFields`). For a group, issue in bulk. They open it from the confirmation email. No more PDF to print. 30 minutes before departure, your scheduling system sends a message to every holder on the sailing: "Boarding in 30 min, dock B6. Remember your ID." Bad weather, one-hour delay: update the times on every ticket of the trip, then explain why with a message. Dock change? A message is enough. New cabin, an extra child, moved to another sailing: update the ticket concerned, including its `tripNumber`. Staff scan the barcode. The ticket is looked up, then redeemed. Count crossings on the loyalty card ("8/10 crossings"). At the threshold, send a cabin upgrade coupon. Before summer, win back last year's passengers with a targeted coupon. ## Best practices - **One `tripNumber` per sailing, not per route.** Monday's 17:30 and Tuesday's 17:30 are two different trips. - **A family: how many tickets?** One ticket per booking with the passenger list on the back works for group boarding. One ticket per passenger is needed if each person is checked separately. - **Warn early.** A delay announced at home avoids a crowd on the quay. Trigger the update as soon as your operations system detects it. - **Set the expiry date.** The ticket expires after the crossing. ## Go further - [Transport & mobility](/use-cases/transport), the same template for buses and trains - [Update a trip](/kb/guides/update-a-trip) - [Push notifications](/kb/push-notifications) - Endpoints: ## Get inspired A crossing is also the start of a holiday. Here are ideas to enrich your passengers’ experience. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Gym membership cards in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/fitness-clubs Members forget their plastic badge half the time. Never their phone. **The member card in Apple Wallet or Google Wallet gets them into the club**, shows their tier and receives your class reminders. You issue it at sign-up, update it when the tier changes, and keep it alive all year: classes, promotions, referrals, shop. ## Customer journey ```mermaid sequenceDiagram autonumber actor M as Member participant L as Club software participant E as eldo wallet participant W as Apple / Google Wallet M->>L: Signs up for Premium L->>E: Creates the member with their type E-->>M: Email with the card M->>W: Adds the member card M->>L: Scan at the front desk L->>E: Looks up the member by barcode E-->>L: Tier, pass status, renewal date L-->>M: Access granted Note over L,E: Day before the class L->>E: Reminder notification W-->>M: Your yoga class is tomorrow at 6 pm ``` ## Integration workflow Pick `MEMBER_CARD`, show the club (`CLUB`) and the member number (`N_MEMBER`). On iPhone, show the tier in the header (`iosHeader` set to `TYPE`). Membership renewal happens on your side: keep a card with no expiry and track the due date with the member's renewal date. Turn off sharing. Essential, Premium, Student, Off-peak: each tier becomes a client type. Your access software uses it to apply your rules (opening hours, clubs included). See [Client types](/kb/concepts/customer-types). Your club management software creates the member as soon as they pay, with their member number as the barcode, their tier, sign-up date and renewal date. To move your current members to the wallet, import them in bulk. See [Import a customer base](/kb/guides/import-customers-csv). They show it at the front desk, even offline. A few days after sign-up, remind members whose card isn't installed. The front-desk scanner reads the barcode. Your access software looks up the member and reads their tier (`clientTypeName`), pass status (`passStatus`) and renewal date (`renewalDate`). It then decides whether to open. An unknown barcode returns `MODEL_NOT_FOUND`. Upgrade to Premium, renewal paid: update the type and the renewal date. The installed card updates. 30 days before the due date, schedule a notification (`type: SCHEDULED`, `category: INFORMATIVE`) for the members concerned (`target: SPECIFIC`). Reminder the day before a booked class, cancelled class, new timetable: a personal message per member. Back-to-school offer or open-house week: create a communication, the card's strip changes for the period. See [Push notifications](/kb/push-notifications). A referred friend signs up: send a coupon to both the referrer and the new member (free month, coaching session). Shaker, protein, towel: a shop coupon for Premium members. At the desk, scan the coupon and redeem it. :::info[Access control stays yours] eldo wallet issues the card, updates it and answers the barcode lookup. The scanner, the turnstile and the decision to open stay in your access-control software. ::: ## Best practices - **Member number as the barcode.** It's the number your software already knows: no mapping table. - **One card for the whole membership.** On renewal, update the existing card rather than issuing a second one. - **Turn off sharing.** The card is personal. - **A 2D scanner at the desk.** 1D laser scanners can't read a QR code on a screen. Test with Apple and Google cards, with screen brightness turned down. - **Classes and timetable: `INFORMATIVE`. Promotions and shop: `COMMERCIAL`.** The category decides who gets the notification, based on their consent. ## Go further - [Sport & clubs](/use-cases/sport) for stadium season passes and match tickets - [Client types](/kb/concepts/customer-types) - [Push notifications](/kb/push-notifications) - [Barcodes](/kb/concepts/barcodes) - [Import a customer base](/kb/guides/import-customers-csv) - Endpoints: ## Get inspired The membership card can motivate members at every session. Here are ideas to keep them for longer. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Health insurance member cards in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/health-insurance You support a member for years. A paper card goes stale the moment their plan changes. **A member card in Apple Wallet or Google Wallet is always up to date:** number, plan, coverage, contact. It serves as proof of membership and gives you a direct channel for renewal reminders and new documents. ## Customer journey ```mermaid sequenceDiagram autonumber actor A as Member participant SI as Insurer back end participant E as eldo wallet participant W as Apple / Google Wallet A->>SI: Signs up for the Comfort plan SI->>E: Creates the member E-->>A: Email with the card A->>W: Adds the member card E->>SI: pass.installed webhook Note over SI,E: Plan change SI->>E: Updates type and coverage E->>W: Card updated Note over SI,E: 60 days before renewal SI->>E: Scheduled notification W-->>A: Your membership renews on 15/09 ``` ## Integration workflow Pick `MEMBER_CARD`, show the member number (`N_ADHERENT`) and the organisation (`ORGANISATION`). Set validity: fixed date, after a period, or none. Add useful contacts (phone, member area) on the wallet. Turn off sharing. Essential, Comfort, Premium: each plan becomes a client type. See [Client types](/kb/concepts/customer-types). Your back end creates the member once the membership is approved, with their number as the barcode, their type, admission date and renewal date. To digitise an existing base, import it in bulk (JSON up to 5,000 rows per call, or a CSV file). See [Import a customer base](/kb/guides/import-customers-csv). They show it when asked, even offline. A week after sending, remind members whose card isn't installed: all of them, or a specific list. Plan change, new renewal date: update the member. Remaining allowances, member-area link, assistance number: add them as additional information on the back. 60 days before the due date, schedule a notification (`type: SCHEDULED`, `category: INFORMATIVE`) for the members concerned. Certificate available, unused dental cover at year end, new teleconsultation service: a personal message per member (`target: SPECIFIC`). Set the wallet's webhook URL to receive `pass.installed`, `pass.uninstalled` and client changes. Your advisor sees "digital card active". See [Webhooks](/docs/webhooks). The referral code is validated: send a coupon to both the referrer and the new member. ## Best practices - **Keep health data off the card.** Plan, member number and allowances are enough. Medical details stay in your secure member area. - **Member number as the barcode.** That's what your advisor or partner will scan. - **Turn off sharing.** The card is personal. - **Useful messages, not sales.** Reminders, documents and remaining cover are `INFORMATIVE`. Respect each member's communication preferences. :::note[National health card] The wallet card does not replace the French national health card (carte Vitale). Whether health professionals and third-party payment schemes accept it as proof depends on your agreements with them. ::: ## Go further - [Associations & non-profits](/use-cases/associations) for another member card - [Client types](/kb/concepts/customer-types) - [Push notifications](/kb/push-notifications) - [Webhooks](/docs/webhooks) - Endpoints: ## Get inspired The member card can become a real everyday link. Here are ideas to support your members. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Hotel bookings in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/hotels The guest booked three months ago. On arrival day, they dig through their email for the address and booking number. **With a booking pass in Apple Wallet or Google Wallet, everything is in one place:** dates, room, perks, address. The pass shows up as the guest nears the hotel, gets the room number at check-in and becomes the channel for your in-stay offers. ## Customer journey ```mermaid sequenceDiagram autonumber actor G as Guest participant P as PMS / booking engine participant E as eldo wallet participant W as Apple / Google Wallet G->>P: Books 3 nights P->>E: Issues the booking with check-in and check-out E-->>G: Email with the link G->>W: Adds the booking W-->>G: Pass suggested near the hotel G->>P: Check-in P->>E: Adds room 204 E->>W: Pass updated P->>E: Sends the in-stay spa offer ``` ## Integration workflow One template per property: title, hotel name, description, included perks, colors, strip, barcode format and the property's location. The location brings the pass to the lock screen on arrival. On confirmation, your booking engine issues the pass with check-in and check-out dates, perks and a free back field (booking number, cancellation terms). For a group or a seminar, issue in bulk. Address, dates and hotel contact are one gesture away. On arrival, your PMS updates the pass with `roomNumber`. The guest sees their room in the wallet. A cancellation? Set the pass to `canceled`. Breakfast, spa, late check-out: send the guest a coupon during their stay. Offer the brand's loyalty card and credit the nights stayed. ## Best practices - **Send the pass at confirmation.** The guest keeps it until the stay, even months later. - **Watch for overlapping dates.** Two overlapping bookings for the same guest are rejected (`OVERLAPPING_DATES`). See [Common API errors](/kb/troubleshooting/common-api-errors). - **Room number at check-in, not before.** It often changes between booking and arrival. - **One strip per property.** A photo of the front helps guests recognise the hotel on arrival. ## Go further - [Locations](/kb/concepts/locations) - [Pass types](/kb/concepts/pass-types) - [Retail & fashion](/use-cases/retail) for the loyalty card - Endpoints: ## Get inspired The booking pass stays with guests before, during and after their stay. Here are ideas to delight them. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Employee badges in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/hr-employees A plastic badge gets printed, lost, reprinted, and rarely comes back on the last day. **An employee badge in Apple Wallet or Google Wallet is issued on day one, updated with every change and deleted on departure.** Its barcode carries the employee ID: your systems read it like a regular badge. The same channel carries internal communications, employee perks and badges for your company events. :::info[What eldo wallet does, what your system does] eldo wallet issues the badge, updates it and answers the barcode lookup. The reader, the access-control software and the time-tracking tool stay yours: they read the badge's barcode, then query eldo wallet or their own database. eldo wallet provides neither access hardware nor a time-tracking tool. ::: ## Employee journey ```mermaid sequenceDiagram autonumber actor S as Employee participant RH as HR system participant E as eldo wallet participant W as Apple / Google Wallet participant A as Your access control RH->>E: Creates the employee, ID as barcode E-->>S: Email with the badge S->>W: Adds the badge S->>A: Shows the badge to the reader A->>E: Looks up the employee by barcode E-->>A: Employee, type, pass status A-->>S: Access or clock-in recorded by your system Note over RH,E: Employee leaves RH->>E: Deletes the employee RH->>A: Deactivates the employee ID ``` ## Integration workflow Pick `MEMBER_CARD`, show the company (`ENTREPRISE`) and the start date (`ADMISSION_DATE`). On iPhone, show the employee's name in the header (`iosHeader` set to `FULLNAME`). Pick a barcode format your readers can scan: `QR_CODE`, `CODE_128`, `PDF_417` or `AZTEC`. Turn off sharing. Employee, temp, intern, contractor: each status becomes a client type. Your access control uses it to apply its rules (zones, hours). See [Client types](/kb/concepts/customer-types). Your HR system creates the employee when they join, with their work email, employee ID as the barcode, status, entity (`companyName`) and start date. At launch, import all employees in bulk. See [Import a customer base](/kb/guides/import-customers-csv). They show it to the reader, even with no network on their phone. Your reader scans the barcode. Your access-control or time-tracking software looks up the employee by ID, checks their type (`clientTypeName`) and pass status (`passStatus`), then opens the door or records the clock-in in its own tool. An unknown ID returns `MODEL_NOT_FOUND`. Seminar, annual convention, open day, customer visit: create an event, then issue a badge to each attendee. Employees: pass their `clientId`. Visitors: first name, last name and email are enough. At reception, scan the badge and redeem it. See [Events & trade fairs](/use-cases/events). Site closed today, evacuation drill, benefits enrolment now open: an `INFORMATIVE` notification to every badge holder, or to a list (`target: SPECIFIC`). For an internal campaign (safety week, charity drive), create a communication: the badge's strip changes for the period. See [Push notifications](/kb/push-notifications). Works council offers, partner discounts, a free meal at the staff restaurant: send a coupon to the employees concerned. The partner or the till scans and redeems it. New status or new site: update the type and the entity. Department, HR contact, emergency number: add them as additional information on the back. The installed badge updates. On the last day, your HR system deletes the employee in eldo wallet, by ID or by employee number. The same day, deactivate the employee ID in your access control: it's the one that decides to open the door. ## Best practices - **Employee ID as the barcode.** It's the identifier your systems already know. - **No sensitive data on the badge.** Name, entity and start date are enough. Pay, reviews and health data stay in your HR system. - **Offboarding happens on both sides.** Delete in eldo wallet and deactivate in your access control, on the same day. - **Plan a fallback mode.** The badge displays offline, but the barcode lookup needs network on the reader side. Keep a local list of active IDs in your access system. - **Test your readers.** A 1D laser reader can't scan a QR code on a screen. Test with Apple and Google badges before rollout. - **Turn off sharing.** The badge is personal. ## Go further - [Events & trade fairs](/use-cases/events) for event tickets - [Barcodes](/kb/concepts/barcodes) - [Client types](/kb/concepts/customer-types) - [Import a customer base](/kb/guides/import-customers-csv) - [Webhooks](/docs/webhooks) - Endpoints: ## Get inspired The Wallet badge can support the whole employee experience. Here are ideas to go further. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Apple Wallet and Google Wallet loyalty cards for retail Source: https://developers.eldowallet.fr/en/use-cases/retail Clothes, shoes or cosmetics: the journey is always the same. **Sign them up, keep them engaged, reward them.** eldo wallet replaces the plastic card with a digital loyalty card that lives on the phone, in Apple Wallet or Google Wallet. Customers find it on their lock screen, watch their balance grow at every checkout and get coupons without opening a single email. ## Customer journey ```mermaid sequenceDiagram autonumber actor C as Customer participant POS as POS / CRM participant E as eldo wallet participant W as Apple / Google Wallet C->>POS: Signs up at checkout or scans the QR code POS->>E: Creates the client E-->>C: Email or SMS with the add link C->>W: Adds the loyalty card C->>POS: Pays 87 euros POS->>E: Updates the balance E->>W: Pushes the new balance W-->>C: Notification with the new balance POS->>E: Tier reached, sends the coupon E->>W: Coupon added C->>POS: Shows the coupon at checkout POS->>E: Redeems the coupon ``` ## Integration workflow There is one loyalty card (store card) per wallet. Set it up once: type (`STORE_CARD` or `MEMBER_CARD`), program name, colors, strip, gain label and unit (`POINTS`, `EURO`…) and the three fields shown on the card. Logo, icon and contacts live on the wallet. Do it in the back-office or through the API. Create one client type per tier: Member, Silver, Gold. See [Client types](/kb/concepts/customer-types). Add the GPS coordinates of your stores and a short message per location, optionally per time slot. When the customer walks by, the card shows up on their lock screen. See [Locations](/kb/concepts/locations). Create one coupon per mechanic: welcome (`isWelcomeCoupon`), birthday, tier reward, win-back. Each coupon is a reusable **template**: name, description, value, promo code, expiry, strip and barcode format. The body is `multipart/form-data`: objects (`colors`, `security`) are sent as JSON strings. On every sign-up (checkout, website, app), your CRM creates the client. eldo wallet generates the card. With `sendCard: true`, eldo wallet sends the add link. Otherwise, take the link from the response and share it through your own channels. For an existing customer base, import in bulk (JSON or CSV). They open the link on their phone. iPhone: Apple Wallet. Android: Google Wallet. One tap, the card is installed. Set a webhook URL on the wallet: you receive `pass.installed` and `pass.uninstalled`. Or query the client list filtered by status. Your sales staff can see who has the card. See [Webhooks](/docs/webhooks). Your POS scans the card and sends the customer's gain, by barcode or by ID. eldo wallet updates the card in the wallet. Birthday today, 500-point tier reached, no purchase in 60 days: your CRM spots the event and sends the coupon. For a known customer, pass their `clientId`. Sending is asynchronous: track the operation by its ID, or pass a `webhookUrl`. The POS scans the barcode and redeems the pass. A second scan is rejected (`PASS_ALREADY_REDEEMED`). Scanned by mistake? Undo the redemption. See [Pass lifecycle](/kb/concepts/pass-lifecycle). Private sale for every card holder, or a personal message per customer from your CRM ("Your Click & Collect order is ready"). For a campaign, create a communication: the card's strip changes for the chosen period. See [Push notifications](/kb/push-notifications). Pull client, coupon and notification stats into your dashboard. Cross them with your average basket and visit frequency. ## Ready-made scenarios | Scenario | Trigger | Calls | | --- | --- | --- | | QR code at the till | Customer scans a QR printed from the back-office | None, no code | | "Wallet card" button in the CRM | Sales assistant clicks on the customer record | | | Welcome coupon | A client signs up | Coupon created with `isWelcomeCoupon` | | Happy birthday | Daily cron at 9 am | | | Live balance | Receipt rung up | | | Private sale | Campaign launch | | | Click & Collect ready | Order marked "ready" | | | We miss you | No purchase in 60 days | | ## Best practices - **One coupon template per mechanic.** Don't create a new coupon for every send. Create "Birthday" once, send it every day. - **Own the link when it matters.** With `sendCard: false`, put the add link in your own welcome email, on your receipt or in your app. - **Redeem server-side, not by hand.** Redemption must come from the POS at scan time. That's what blocks double use. - **Respect consent.** Every notification is `INFORMATIVE` or `COMMERCIAL`. A promotion is commercial: it only reaches customers who accept it. - **Space out your notifications.** One useful notification a week beats three promotions. Customers can turn off the pass's notifications, or delete it. - **Test on both platforms.** Rendering differs between Apple Wallet and Google Wallet. See [Apple Wallet or Google Wallet](/kb/concepts/apple-vs-google-wallet). ## Go further - [Templates and passes](/kb/concepts/templates-vs-passes) - [Import a customer base](/kb/guides/import-customers-csv) - [Send a coupon to non-clients](/kb/guides/send-to-non-clients) - [Client types](/kb/concepts/customer-types) for Silver, Gold, VIP cards - [A pass is not updating](/kb/troubleshooting/pass-not-updating) - Endpoints: ## Get inspired Beyond the points balance and coupons, here are ideas retail brands love. Enough to surprise your customers and bring them back. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Season passes and match tickets in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/sport A fan wants three things: **to be in the stadium, to know the line-up before anyone else, to wear the shirt.** eldo wallet replaces the plastic season card with a card in Apple Wallet or Google Wallet. It opens the turnstile, gets the line-up the day before the match and receives club store offers. ## Customer journey ```mermaid sequenceDiagram autonumber actor S as Fan participant C as Club ticketing participant E as eldo wallet participant W as Apple / Google Wallet S->>C: Renews the season pass C->>E: Creates the client with type Season holder E-->>S: Email with the card S->>W: Adds the season card Note over C,E: Day before the match C->>E: Starting XI notification W-->>S: Tomorrow 5 pm vs Reims S->>C: Scan at the turnstile C->>E: Checks the card and counts attendance C->>E: 10th match, sends the store coupon ``` ## Integration workflow The season card is the wallet's member card: type `MEMBER_CARD`, program name, club colors, member number (`N_MEMBER`) and club (`CLUB`) in the displayed fields. Set validity to the end of the season. Season holder, premium holder, free supporter: each plan becomes a client type. See [Client types](/kb/concepts/customer-types). When the season pass is confirmed, your ticketing system creates the fan with their type and renewal date. The card's barcode is the entry ticket. At the start of the season, create all holders in bulk. Cup ties, friendlies: create one event per match and issue the ticket. Pass the fan's `clientId` to link it to their record. See [Events & trade fairs](/use-cases/events). Line-up the day before, 2-hour reminder with the entrance to use, new shirt in the store: a notification to every holder or a personal message. For a store offer, create a communication: the card's strip changes for the period. The turnstile reads the card's barcode. Your system finds the holder and checks their type and validity. Your club counter goes up on every scan. At the 10th match, send the store coupon. 30 days before the season pass ends, schedule a notification (`type: SCHEDULED`) for the holders concerned. On renewal, update the holder's date and type. ## Best practices - **One card, many seasons.** On renewal, update the existing card instead of creating a second one. - **Turn off sharing** on the season card so it doesn't travel between phones. - **Target notifications.** Premium holders and free supporters don't expect the same messages. - **Line-ups and times: `INFORMATIVE`. Store: `COMMERCIAL`.** The category decides who receives the notification based on their consent. - **Prepare match day.** Test turnstiles with Apple and Google cards before the first home game. ## Go further - [Gyms & fitness clubs](/use-cases/fitness-clubs) for a gym member card - [Client types](/kb/concepts/customer-types) - [Push notifications](/kb/push-notifications) - [Pass lifecycle](/kb/concepts/pass-lifecycle) - Endpoints: ## Get inspired A season card lives all season long. Here are ideas to make it indispensable to your fans. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ### Transit tickets in Apple Wallet and Google Wallet Source: https://developers.eldowallet.fr/en/use-cases/transport When you sell travel, your customer needs one thing: **the right ticket, at the right time, without stress.** eldo wallet puts the ticket in Apple Wallet or Google Wallet on their phone at booking. It works offline, shows up on the lock screen on departure day and updates itself when the schedule changes. ## Customer journey ```mermaid sequenceDiagram autonumber actor V as Traveller participant B as Ticketing system participant E as eldo wallet participant W as Apple / Google Wallet V->>B: Books Mulhouse - Geneva B->>E: Issues the ticket with its tripNumber E-->>V: Email or SMS with the link V->>W: Adds the ticket Note over B,E: The day before departure B->>E: Earlier departure for this tripNumber E->>W: Updates every ticket on the trip W-->>V: Departure 09:00 instead of 09:30 V->>B: Shows the ticket to the inspector B->>E: Validates the barcode ``` ## Integration workflow A transit template holds what all tickets on a route share: type (`Bus`, `Train`, `Tram`, `Boat`, `Air`, `Generic`), stations and codes, reference times, fare, terms, customer service number, colors, strip and barcode format. Create it once, in the back-office or through the API. The wallet must have the **TRANSIT** pass type enabled. On your website, app, at the counter or through a reseller. Your ticketing system records the sale, then calls eldo wallet. One call per traveller, with their contact details, dates, barcode and above all the **`tripNumber`**. This number groups every ticket on the same service: it's what makes bulk updates possible. Two delivery options: - `enableSendEmailOrSms: true`: eldo wallet sends the email or SMS with the "Add to Wallet" button. - `enableSendEmailOrSms: false`: you take the link from the response and share it yourself (confirmation email, app, QR code on a travel-agency ticket or a kiosk). School groups, company outings, charters: send up to 5,000 tickets in one request. The operation runs asynchronously. Track its status by ID, or pass a `webhookUrl` to be notified when it's done. One call updates **every ticket on the trip** (times, stations, dates). To explain why ("30 min delay, weather"), also send a message to the `tripNumber` holders. Full guide: [Update a trip](/kb/guides/update-a-trip). New date for one traveller, new seat, typo in the name: update their ticket without touching the others. Several fixes at once: use the bulk update. The inspector scans the barcode. Your terminal looks up the ticket, then redeems it. A second scan is rejected. Turn on one-time use on the template to block a second boarding. List the tickets issued per route (`page` and `limit` pagination) and follow stats by route, type and period. Add a traveller card: a trip counter on the loyalty card, and a "free trip" coupon on the 5th journey. ## Best practices - **The `tripNumber` is essential in practice.** Without it, you'll have to update each ticket one by one when a delay hits. - **Set an expiry date.** The ticket expires after the trip and stays in the wallet history as a receipt. - **Separate data and message.** Update the time with one call, explain the reason with another. Travellers understand what changed and why. - **Dates as `YYYY-MM-DD`, times as `HH:mm`.** If you change a trip's departure date, send the arrival date too. - **Guard against sharing.** The template's security options (one-time use, iOS sharing off, rotating barcode) limit resale and screenshots. ## Go further - [Update a trip](/kb/guides/update-a-trip) - [Ferry & maritime](/use-cases/ferry), same mechanics with cabins and docks - [Airlines](/use-cases/airlines), same mechanics with the `Air` type - [Barcodes](/kb/concepts/barcodes) - [Pass lifecycle](/kb/concepts/pass-lifecycle) - Endpoints: ## Get inspired The ticket is only the start of the journey. Here are ideas to look after your travellers and bring them back. Like one of these ideas? [Contact our team](https://www.eldowallet.fr/contact/) and we'll show you how to set it up. ## Knowledge base ### Apple Wallet and Google Wallet knowledge base Source: https://developers.eldowallet.fr/en/kb/ # Apple Wallet and Google Wallet knowledge base Integrating eldo wallet, or running a programme day to day? Start here. **Concepts** set the vocabulary. **Guides** walk through one task end to end. The **FAQ** and **troubleshooting** pages answer the questions that keep coming back. ## Concepts ## How-to guides ## FAQ and troubleshooting :::tip[New here?] Run through the [quickstart](/docs/quickstart) first, then browse the [use cases](/use-cases/retail) for your industry. ::: ### Apple Wallet vs Google Wallet: the differences Source: https://developers.eldowallet.fr/en/kb/concepts/apple-vs-google-wallet # Apple Wallet vs Google Wallet: the differences You make **one call** to eldo wallet. eldo wallet builds both the Apple Wallet and the Google Wallet version of the pass. The customer opens their link and the phone picks the right platform. Nothing to wire up on either side. The two platforms still behave differently. Here is what you need to know. ## At a glance | | Apple Wallet | Google Wallet | |---|---|---| | Devices | iPhone, Apple Watch | Android phones | | Format | Signed `.pkpass` file | Object hosted by Google (one *class* + one *object* per holder) | | Updates | The server pings the device (APNs), the device downloads the new version | Google updates the object and pushes it to the device | | Notification | Shown when a field configured to notify changes value | Message attached to the pass, or update notification | | Lock screen | Pass suggested based on relevant location or date | Pass suggested based on location and event date | | Logo | `logo` | `androidLogo` (dedicated logo, shown in a circle) | ## What eldo wallet handles for you - **Signing** `.pkpass` files and publishing Google Wallet classes and objects. - **Device registration**: when the customer adds the pass, their device subscribes to updates. - **Update delivery**: you change a customer, a trip or a template, eldo wallet relays it to both platforms. - **One add link**: the same link opens Apple Wallet on iPhone and Google Wallet on Android. ## Differences that affect you ### The rendering is not identical Apple and Google use different field layouts. A field on the front on iPhone may show on the back, or in the details, on Android. The iOS header has its own settings (`iosHeader` on the loyalty card, coupons and transits). Always test on both platforms before launch. ### Sharing is set per platform The loyalty card has `sharingProhibited` (iOS) and `sharingProhibitedAndroid`. See . ### Notifications are delivered differently On Apple Wallet, a notification appears when a displayed value changes and that field is set to notify. On Google Wallet, it goes through a message attached to the pass. eldo wallet hides the difference: you send one notification, eldo wallet picks the right mechanism. See [Push notifications](/kb/push-notifications). ### Google Wallet errors have their own codes Some errors come from Google Wallet itself: `CLASS_NOT_FOUND`, `OBJECT_CREATION_FAILED`, `OBJECT_UPDATE_FAILED`. See the [error reference](/docs/errors). ## Go further - [Templates and passes](/kb/concepts/templates-vs-passes) - [Pass design](/kb/concepts/design-guidelines) - [Pass not updating](/kb/troubleshooting/pass-not-updating) ### Wallet pass barcodes: QR, Aztec, PDF417 Source: https://developers.eldowallet.fr/en/kb/concepts/barcodes # Wallet pass barcodes The barcode connects the pass to your systems: till, handheld scanner, gate. Two decisions to make: 1. **The format**, set on the template: `barcodeFormat` (coupon, transit) or `barcodeType` (loyalty card, event). 2. **The value**, specific to each pass: `barcode`. ## Available formats | Format | Shape | When to use it | |---|---|---| | `QR_CODE` | 2D square | The default. Read by almost every scanner and smartphone. | | `AZTEC` | Compact 2D square | Transit and airline ticketing. Reads well on a screen, even a small one. | | `PDF_417` | Stacked 2D rectangle | Boarding passes, access-control systems that require it. | | `CODE_128` | 1D linear code | Tills with 1D scanners, loyalty cards. | | `NONE` | No barcode | Information-only pass (confirmation, member card without checks). | :::tip[Test with your scanners] Before launch, scan a real pass on a real phone with **your** hardware. An old 1D scanner cannot read a QR code. ::: :::note[Apple detail] Apple Wallet does not show 1D codes (`CODE_128`) on Apple Watch. Use a 2D format if your customers scan from their watch. ::: ## The barcode value - By default, eldo wallet generates a value. - You can set your own (CRM customer number, ticket number from your ticketing system): `barcode` when you create the customer or the pass. - The barcode then works as a **key**: look up a customer (), add points at the till (), find a ticket (, ), redeem a pass (). :::warning[Uniqueness] The same barcode cannot be given to two customers of the same wallet. The API returns `BARCODE_ALREADY_EXISTS` (409). ::: ## Security options Templates expose a `security` block: | Option | Effect | |---|---| | `enableOneTimeUse` | The pass works once. | | `disableIOSPassSharing` | The holder cannot share the pass from Apple Wallet. | | `disablePassLinkSharing` | Disables sharing of the pass's add link. | | `disableMultipleHolders` | One holder per pass. | | `enableSecurityAnimation` | Visual animation on the pass, harder to fake with a screenshot. | | `enableRotatingBarcode` | Barcode that changes regularly: a screenshot becomes useless. | In `multipart/form-data`, send the block as a JSON string: `security={"disableIOSPassSharing":true}`. Test each option on Apple Wallet and on Google Wallet before launch. ## Go further - [Pass lifecycle](/kb/concepts/pass-lifecycle) - [Events use case](/use-cases/events) ### Customer types Source: https://developers.eldowallet.fr/en/kb/concepts/customer-types # Customer types Not all holders are the same: a VIP, a trade customer, an employee, a regular customer. **Customer types** sort your holders into segments you define. ## What they are for - **Show a status** on the card: "Gold", "Pro", "Season ticket". The iOS header can show the type (`iosHeader` set to `TYPE`, see ). - **Target** a segment: send coupons and notifications to its customers (recipient list, `target: SPECIFIC` for notifications). - **Move customers up**: Silver → Gold, you change their type, their card updates. ## Endpoints | Need | Endpoint | |---|---| | List types | | | Create a type (and make it the default with `isDefault`) | | | Read, edit, delete a type | , , | | Assign a type to a customer | `clientType` on or | ## The default type A wallet that uses types **always has a default type**. A customer created without a type gets it. You cannot remove the only default type: the API returns `DEFAULT_CLIENT_TYPE_REQUIRED`. Make another type the default first. :::warning[Assignment error] An unknown or malformed type when creating a customer returns `ERROR_CUSTOMER_TYPE_ASSIGNMENT`. Get the ids with . ::: ## Go further - [Import a customer base](/kb/guides/import-customers-csv) - [Automotive use case](/use-cases/automotive): consumers and trade customers in one programme ### Apple Wallet and Google Wallet pass design Source: https://developers.eldowallet.fr/en/kb/concepts/design-guidelines # Apple Wallet and Google Wallet pass design A pass gets two seconds of attention, in sunlight, at a till. It must be **readable before it is pretty**. ## Visual elements | Element | Role | Where to set it | |---|---|---| | **Logo** | Your brand, top of the pass (iOS) | (`logo`) | | **Android logo** | Logo shown in a circle on Google Wallet | (`androidLogo`) | | **Icon** | Shown in notifications and on the lock screen | (`icon`) | | **Strip** | Image banner under the header: the offer, the season, the event | `strip` on each template, `overrideStrip` for one customer () | | **Colours** | `backgroundColor` (background), `foregroundColor` (values), `labelColor` (labels) | `colors` block on each template | ## API constraints - Images in **PNG or JPEG**, from **1 KB to 5 MB**. - Sent as `multipart/form-data`. The `colors` block goes in as a JSON string: `colors={"backgroundColor":"#131313","foregroundColor":"#FFFFFF","labelColor":"#9EE00D"}`. See [Files and images](/docs/files-and-images). ## Platform recommendations :::note[Source: Apple Human Interface Guidelines and Google Wallet documentation] The sizes below are Apple’s and Google’s **public** recommendations, not eldo wallet API constraints. ::: **Apple Wallet** (in points, provide @2x or @3x resolution): | Image | Recommended size (@1x) | |---|---| | Icon | 29 × 29 pt | | Logo | 160 × 50 pt max | | Strip (loyalty card, coupon, event ticket) | about 375 × 123 pt | **Google Wallet**: | Image | Recommendation | |---|---| | Logo | Square, at least 660 × 660 px, shown in a circle | | Hero image (strip equivalent) | 1032 × 336 px | ## Rules that avoid bad surprises - **Contrast.** Light text on dark, or the other way round. Test in sunlight. - **Nothing essential in the strip.** Each platform crops it differently. A price or date written in the image may disappear: put it in a field. - **Logo on a transparent background.** A logo on a white box clashes with a coloured pass. - **Centre the Android logo.** It is cropped to a circle: leave a margin. - **No tiny text.** The pass is shown small, sometimes on a watch. - **One strip per campaign, not per day.** Changing the visual updates every card. - **Test on both platforms.** See [Apple Wallet vs Google Wallet](/kb/concepts/apple-vs-google-wallet). ## Go further - [Files and images](/docs/files-and-images) - [Templates and passes](/kb/concepts/templates-vs-passes) ### Locations and proximity alerts Source: https://developers.eldowallet.fr/en/kb/concepts/locations # Locations and proximity alerts The customer walks past your store and their card pops up on the lock screen with a message: "10% off today, just round the corner". You send nothing. The phone triggers it, based on the locations stored in the pass. ## How it works 1. You declare **locations**: latitude, longitude, and a **relevant text** (the message shown). 2. eldo wallet writes them into the passes. 3. When the customer enters the area, Apple Wallet or Google Wallet suggests the pass on the lock screen with that text. ## Endpoints | Need | Endpoint | |---|---| | List the loyalty card locations | | | Add a location | | | Edit, delete | , | | Locations for an event, transit or hotel | `locations` (or `location`) field on the template: , , | ## Messages by time of day A location can carry **several texts**, each with its own time slot: `relevantTextList`, with `relevantText`, `startTime` and `endTime`. Example: "Free coffee before 10 am" in the morning, "Happy hour until 7 pm" in the afternoon, same spot. ## Limits - **A maximum number of locations per wallet.** Beyond it, the API returns `MAX_WALLET_LOCATIONS_REACHED`. Apple Wallet itself only uses **10 locations per pass**. - **It is not a push.** The system decides whether to show the pass based on distance, phone usage and the customer’s settings. Do not rely on it for critical information. - **The customer stays in control.** They can turn off pass suggestions on the lock screen. :::tip[More than 10 stores] Pick the 10 places that matter most: flagship stores, stations, shopping centres. The rest will not show on iPhone. ::: ## Go further - [Push notifications](/kb/push-notifications) - [Retail use case](/use-cases/retail) - [Hotels use case](/use-cases/hotels) ### Wallet pass lifecycle Source: https://developers.eldowallet.fr/en/kb/concepts/pass-lifecycle # Wallet pass lifecycle A pass goes through a few simple states. Knowing them avoids two classic mistakes: chasing a customer who already has the card, and accepting the same coupon twice. ```mermaid stateDiagram-v2 [*] --> Sent: customer or pass created Sent --> Installed: customer adds the pass to their Wallet Installed --> Uninstalled: customer removes the pass Uninstalled --> Installed: customer adds it again Installed --> Redeemed: redeem (scanned at the till or the door) Redeemed --> Installed: unredeem (scan cancelled) Installed --> Expired: expiry date reached Redeemed --> Expired Expired --> [*] ``` ## The states | State | What happened | How to know | |---|---|---| | **Sent** | eldo wallet created the pass and its link. The email or SMS went out if you asked for it. | Response of the create call | | **Installed** | The customer added the pass to Apple Wallet or Google Wallet. Their device now gets updates. | `pass.installed` webhook, `status` filter on | | **Uninstalled** | The customer removed the pass from their phone. | `pass.uninstalled` webhook | | **Redeemed** | The pass was scanned and validated. | | | **Expired** | The expiry date has passed. The pass stays in the Wallet, marked as expired. | Pass expiry date | Webhooks are set at wallet level. See [Webhooks](/docs/webhooks). ## Nudge inactive customers A customer got their card but never installed it? reminds every inactive customer (`all`) or a given list (`specific`). ## Redeem and unredeem At scan time, your till or access control calls with the **template** id and the scanned **barcode**. The pass becomes "redeemed". A second scan of the same pass is rejected with `PASS_ALREADY_REDEEMED`. Cashier made a mistake? Cancel with . On a pass that was never redeemed, the API returns `PASS_NOT_REDEEMED`. :::tip[One source of truth] Let eldo wallet validate the pass, not just your till. The status updates in the customer’s Wallet and in your stats. ::: ## Set a status directly | Pass | Endpoint | Statuses | |---|---|---| | Coupon | | `UNUSED`, `USED`, `EXPIRED` | | Coupon (expire now) | | | | Transit | | `UNUSED`, `USED` | | Event | | | ## Expiry - **Loyalty card**: `validity` block on . `expirationType` is `NO_EXPIRATION`, `AT_DATETIME` (fixed date) or `AFTER_PERIOD` (a duration after creation, in years, months or weeks). - **Coupon**: `expirationDate` on the template, overridable per customer when sending. - **Transit**: `expirationDate` on the pass, or for a whole trip with . Once expired, the pass stays greyed out in the customer’s Wallet as a receipt. The `status` filter on accepts `active`, `inactive`, `deleted`, `canceled` and `expired`. ## Go further - [Barcodes](/kb/concepts/barcodes) - [Push notifications](/kb/push-notifications) - [Webhooks](/docs/webhooks) ### Apple Wallet and Google Wallet pass types Source: https://developers.eldowallet.fr/en/kb/concepts/pass-types # Apple Wallet and Google Wallet pass types eldo wallet offers five pass types. Pick based on **what the customer does with the pass**, not on your industry. | Type | The customer uses it to… | Examples | Use cases | |---|---|---|---| | **Loyalty card** (store card) | Identify themselves, earn points or cashback, prove membership | Retail card, garage card, member card | [Retail](/use-cases/retail), [Automotive](/use-cases/automotive), [Health insurance](/use-cases/health-insurance), [Sport](/use-cases/sport) | | **Coupon** | Get a discount, once or several times | Birthday -20 %, free trip | [Retail](/use-cases/retail) | | **Event** | Get into a venue on a given date | Concert, trade fair, match, cinema screening | [Events](/use-cases/events), [Sport](/use-cases/sport), [Cinema](/use-cases/cinema) | | **Transit** | Board | Bus, coach, train, tram, ferry, plane | [Transport](/use-cases/transport), [Ferry](/use-cases/ferry), [Airlines](/use-cases/airlines) | | **Hotel** | Find their booking, dates and room | 3 nights, room 204 | [Hotels](/use-cases/hotels) | The pass type must be **enabled** on your wallet (`allowedPasses`, see ). Otherwise the API rejects the template. ## Loyalty card: `STORE_CARD` or `MEMBER_CARD` One card per wallet, set with : - `STORE_CARD`: a store card, with the balance up front (points, euros, dollars, dirhams). - `MEMBER_CARD`: a member card, built around the holder’s identity (member number, licence, plate number…). Update a customer’s balance with , or from the till by barcode with . ## Transit: one template, six modes The transit template takes a `type`: `Bus`, `Boat`, `Train`, `Air`, `Tram` or `Generic`. The same integration covers a coach, a regional train, a crossing or a flight. The pass accepts a flight number, a gate and a seat. ## Single or multi-instance coupon A coupon can be issued once per customer, or as **several instances** for the same customer (`isMultipleInstance`), each with its own barcode. A coupon can also be the **welcome coupon** (`isWelcomeCoupon`), sent on every new sign-up. Only one active welcome coupon at a time. ## Go further - [Templates and passes](/kb/concepts/templates-vs-passes) - [Barcodes](/kb/concepts/barcodes) ### Templates and passes Source: https://developers.eldowallet.fr/en/kb/concepts/templates-vs-passes # Templates and passes Two objects shape the whole eldo wallet API: - **The template**: the model. It holds the branding and the information shared by every holder. You create it **once**. - **The pass**: one customer’s card. It holds their personal data: name, seat, barcode, balance. You issue **one per customer**. ```mermaid flowchart LR T["Template
colours, strip,
terms, barcode format"] T --> P1["Pass · Marie
barcode, seat 12"] T --> P2["Pass · Loïc
barcode, seat 13"] T --> P3["Pass · Claire
barcode, seat 14"] ``` ## Example A concert is **one** event template: name, venue, date, official strip, colours. Each attendee gets **their own** pass: gate, row, seat, barcode. ## What lives where | Information | Template | Pass | |---|---|---| | Colours, strip | ✅ | | | Barcode format | ✅ | | | Security options (sharing, one-time use…) | ✅ | | | Terms, contact | ✅ | | | Holder name and email | | ✅ | | Barcode value | | ✅ | | Seat, trip, dates, balance for this customer | | ✅ | ## Templates by pass type | Type | Create the template | Issue a pass | Issue in bulk | |---|---|---|---| | Coupon | | | (same endpoint, async) | | Event | | | | | Transit | | | | | Hotel | | | | :::warning[Content-Type] Template creation accepts an image (the *strip*), so it is sent as **`multipart/form-data`**. Nested objects (`colors`, `security`, `locations`…) go in as a **JSON string** in the form field, booleans as `"true"` / `"false"`. Issuing passes uses `application/json`. See [Common API errors](/kb/troubleshooting/common-api-errors). ::: ## What about the loyalty card? The loyalty card is a special case: **one per wallet**. Its template is the *store card*. - Set its design with : `cardType` (`STORE_CARD` or `MEMBER_CARD`), programme name, colours, strip, earning unit, displayed fields. - Every customer created with gets the card (with `sendCard`, eldo wallet sends them the link). - [Customer types](/kb/concepts/customer-types) sort the card's holders into segments. ## Changing a template later A template change flows to the passes already issued: new strip, and every card changes its look. Do it for a real campaign, not every day. ## Go further - [Pass types](/kb/concepts/pass-types) - [Pass lifecycle](/kb/concepts/pass-lifecycle) - [Files and images](/docs/files-and-images) ### Apple Wallet and Google Wallet FAQ Source: https://developers.eldowallet.fr/en/kb/faq/general # Apple Wallet and Google Wallet FAQ ## Does the customer need to install an app? No. Apple Wallet ships with every iPhone. Google Wallet is available on Android. The customer opens their link, adds the pass, done. ## Does one link work on both iPhone and Android? Yes. The add link detects the device and offers Apple Wallet or Google Wallet. ## Does the pass work offline? Yes. Once added, the pass displays offline, barcode included. Updates arrive when the phone is back online. ## Which pass types can I issue? Loyalty card, coupon, event, transit (bus, train, tram, ferry, plane…) and hotel. See [Pass types](/kb/concepts/pass-types). ## Can I do everything without code? Much of it, yes. The eldo wallet back office covers card design, the in-store sign-up QR code, notifications to all holders, locations and stats. The API connects your systems: CRM, till, ticketing. The [use cases](/use-cases/retail) show what needs an integration. ## Who sends the email or SMS to the customer? Your choice: - **eldo wallet** sends it: `sendCard: true` when creating a customer, `enableSendEmailOrSms: true` when issuing a transit ticket. - **You** keep the link and send it through your own channel: transactional email, SMS, app, printed QR. The default channel (email, SMS or both) and the rules per message type are set in . ## What happens when the customer changes phone? The pass follows the customer’s account: iCloud backup on Apple, Google account on Android. It does not move across platforms: a customer switching from iPhone to Android opens their add link again. ## I already have cards with another provider. Will my customers lose them? No. Your customers keep their card number and their points. If your Apple Developer and Google Wallet accounts are in your name, their installed cards are taken over with nothing to reinstall; otherwise, they get a new card with the same data. See [Switch wallet providers](/blog/switch-wallet-provider). ## Can I update a pass that is already installed? Yes, that is the point. Change the data in eldo wallet (customer, balance, trip, template): the pass updates on the phone. See [Pass lifecycle](/kb/concepts/pass-lifecycle). ## How do I stop a customer sharing their pass? Turn on the template security options (`disableIOSPassSharing`, `disableMultipleHolders`, `enableRotatingBarcode`…), and `sharingProhibited` / `sharingProhibitedAndroid` on the loyalty card. See [Barcodes](/kb/concepts/barcodes). ## How do I know who actually added their card? Receive `pass.installed` and `pass.uninstalled` events by [webhook](/docs/webhooks), or filter customers by status with . gives the totals. ## How do I handle marketing consent? Each customer has communication preferences: channels per message type, and informative or commercial notifications. See and [Push notifications](/kb/push-notifications). ## How many customers per wallet? Up to 100,000. Beyond that, the API returns `SYSTEM_DOES_NOT_SUPPORT_MORE_THAN_100000_CUSTOMERS`. ## How many locations per pass? Apple Wallet uses 10 at most. See [Locations](/kb/concepts/locations). ## Is there a test environment? Yes, free and with no commitment. See [Environments](/docs/environments). ## How much does eldo wallet cost? Pricing depends on your volumes (number of cardholders, sends) and on the features you need. The test environment is free. [Contact our team](https://www.eldowallet.fr/contact/) for a proposal tailored to your project. ## Which languages does the API answer in? The `x-lang` header picks the response language. See [Localization](/docs/localization). ### Import a customer base Source: https://developers.eldowallet.fr/en/kb/guides/import-customers-csv # Import a customer base You are launching your programme with an existing base: a Salesforce or HubSpot export, or a plain CSV file. Do not create customers one by one: import them **in bulk**. ## Two paths | | CSV import | Bulk create (JSON) | |---|---|---| | Endpoint | | | | Format | CSV file as `multipart/form-data` | `items` array as `application/json` | | Best for | A one-off migration from an export | A recurring sync from your CRM | | Tracking | | or `webhookUrl` | ## Before you start - The loyalty card is configured (). See [Templates and passes](/kb/concepts/templates-vs-passes). - [Customer types](/kb/concepts/customer-types) exist if you segment your base. - A valid API token. See [Authentication](/docs/authentication). ## Step 1 · Prepare the file One row per customer. Useful columns: first name, last name, email (required), phone, and if you have them: barcode (your customer number), customer type, starting balance, birth date, renewal date, admission date, company. | Rule | Why | |---|---| | One unique email per customer | An email already in the wallet is rejected (`EMAIL_ALREADY_EXISTS`). | | One unique barcode | Two customers never share a barcode (`BARCODE_ALREADY_EXISTS`). | | Phone in international format (`+33…`) | Required for SMS. | | UTF-8 encoding | Accented names stay readable on the card. | For the exact file template (column names, separator), contact [eldo wallet support](#support). ## Step 2 · Upload the file :::warning[Let curl write the Content-Type] With `-F`, curl sends `multipart/form-data` with its `boundary`. Do not add a `Content-Type` header yourself. ::: The import runs **in the background**. Track it with . ## Step 3 · Decide who sends the cards `sendNotificationsToCustomers` decides what happens after the import: | Value | What happens | Best for | |---|---|---| | `"true"` | eldo wallet sends each customer their add-to-Wallet link. | A launch with immediate activation. | | `"false"` | Records are created, nobody is notified. | A silent migration, then your own campaign when you choose. | On the JSON path (), make the same choice per customer with `sendCard`. ## Alternative · Bulk create in JSON - Up to **5,000 items** per call, processed **asynchronously**. - The response holds the operation id: track it with , or wait for the call on `webhookUrl`. - `forceSync: true` forces synchronous processing, rejected from 1,000 items (`SYNC_EXECUTION_NOT_ALLOWED`). ## Step 4 · Handle rejects Check the operation result: customers created, duplicates, invalid rows. Fix the rejected rows and resend **only** those. Resending the whole file produces duplicate errors. ## Step 5 · Track activation - : number of customers and installed cards. - : paginated, filterable list (`status`, `createdAtStart`, `search`…). - : nudge those who have not installed their card. - [Webhooks](/docs/webhooks): get `pass.installed` when a customer adds their card. :::note[Maximum volume] A wallet holds up to 100,000 customers (`SYSTEM_DOES_NOT_SUPPORT_MORE_THAN_100000_CUSTOMERS`). ::: ## Go further - [Retail use case](/use-cases/retail) - [Email or SMS not received](/kb/troubleshooting/email-not-received) - [Common API errors](/kb/troubleshooting/common-api-errors) ### Send to non-customers Source: https://developers.eldowallet.fr/en/kb/guides/send-to-non-clients # Send to non-customers A partner hands you a list of prospects. A trade fair gives you 300 press invitations. These people have no loyalty card with you. You still send them a coupon or a ticket, **without signing them up first**. ## Customer or non-customer? | | Customer | Non-customer | |---|---|---| | Record in the wallet | Yes, created beforehand () | No | | Identified by | `clientId` | `firstName`, `lastName`, `email` (required), `phoneNumber` | | Typical case | Birthday coupon, tier reward | Prospect campaign, invitations, partners | ## By pass type | Pass | Endpoint | How | |---|---|---| | Coupon | | In `clients`, give a `clientId` for a customer, or first name, last name and email for a non-customer. Both can be mixed in one send. | | Event | | First name, last name, email, and the seat (`leftRow`, `centerRow`, `rightRow`). | | Event, in bulk | | Same, up to 5,000 tickets, asynchronous. | | Transit, hotel | , | These passes are always issued from the traveller’s contact details. | ## Example: a coupon for prospects Each recipient can have **their own** value, description, expiry date, promo code or barcode. Here, 10% for a cold lead, 20% for a warm one: The send is a **bulk operation**: the response gives the operation id, to track with or via `webhookUrl`. :::warning[Missing details] Without `clientId`, first name, last name and email are required. Otherwise: `MISSING_RECIPIENT_DETAILS_FOR_NON_CLIENT`. ::: ## Best practices - **Have consent.** Emailing or texting a non-customer is still prospecting. Check the legal basis for your list. - **One barcode per voucher** if your till needs to know which voucher was used. - **Measure.** Track coupon usage with . ## Go further - [Events use case](/use-cases/events) - [Retail use case](/use-cases/retail) - [Email or SMS not received](/kb/troubleshooting/email-not-received) ### Delays and cancellations: update a trip Source: https://developers.eldowallet.fr/en/kb/guides/update-a-trip # Delays and cancellations: update a trip The coach leaves 30 minutes late. The ferry moves to another berth. You have 200 passengers on that departure. You do not update 200 tickets: you update **one trip**. ## The key: `tripNumber` `tripNumber` groups every ticket for the same service (one departure, one date). Set it **on every ticket you issue** ( or ). No `tripNumber`, no group update. :::tip[One tripNumber per departure] Use a unique id per dated departure (line + date + time, e.g. `MUL-GVA-20261112-0930`), not the line number alone. Otherwise a Monday delay also changes Tuesday’s tickets. ::: ## The flow ```mermaid sequenceDiagram participant R as Operations / ticketing participant E as eldo wallet participant W as Apple / Google Wallet participant V as Passenger R->>E: PATCH /transits/{transitId}/passes/trip/{tripNumber} E->>W: update every ticket on the trip W->>V: updated ticket + notification R->>E: POST /transits/{transitId}/passes/trip/{tripNumber}/notify E->>W: text message W->>V: "30 min delay due to weather" ``` ## Step 1 · Update the data takes new departure and arrival times and dates, origin and destination stations, and the expiry date. Every ticket on the trip updates. The passenger sees the new time without doing anything. :::warning[Dates] If you change `departureDate`, send `arrivalDate` too. Arrival must stay after departure (`ARRIVAL_TIME_MUST_BE_AFTER_DEPARTURE_TIME_CONSIDERING_DATES`). ::: ## Step 2 · Explain A new time does not say why. Add a message with : ## Special cases | Situation | What to do | |---|---| | Delay or earlier departure | on the times, then a message. | | Platform or berth change | A message with . | | Station change | `originName` / `originStationCode` in . | | One passenger only (seat, name) | on their ticket. | | Several tickets, different values | | | The line template changes (terms, visual) | on the template. | | Departure cancelled | A message, then remove the tickets with if needed. | ## Go further - [Transport use case](/use-cases/transport) - [Ferry use case](/use-cases/ferry) - [Push notifications](/kb/push-notifications) ### Apple Wallet and Google Wallet push notifications Source: https://developers.eldowallet.fr/en/kb/push-notifications # Apple Wallet and Google Wallet push notifications A card in Apple Wallet or Google Wallet is a direct line to your customer’s lock screen. No app to install, no email lost in an inbox. ## One endpoint, four settings covers every case. Four fields drive the behaviour: | Field | Values | Effect | |---|---|---| | `target` | `ALL`, `SPECIFIC` | Every holder, or only the customers listed in `clients` | | `type` | `INSTANT`, `SCHEDULED` | Right now, or at `scheduleAt` (required with `SCHEDULED`) | | `category` | `INFORMATIVE`, `COMMERCIAL` | Service information or marketing: eldo wallet checks the customer’s consent for each category at send time | | `clients` | `id`, `title`, `message` | With `SPECIFIC`: the recipients, each with **their own** title and message if needed | `defaultTitle` and `defaultMessage` apply to every recipient without a custom text. ```mermaid sequenceDiagram participant S as Your server participant E as eldo wallet participant A as Apple / Google participant C as Customer phone S->>E: POST /push-notifications E->>E: filter on consent (category) E->>A: pass updates A->>C: lock-screen notification S->>E: GET /push-notifications/{id}/stats ``` ## Example: a scheduled announcement to everyone ## Example: one message per customer ## Consent: informative or commercial Each customer has preferences: `pushInformativeEnabled` and `pushCommercialEnabled`. A `COMMERCIAL` notification does not reach a customer who opted out of marketing. Read and change preferences with and . :::warning[Classify honestly] "Your order is ready" is informative. "30% off this weekend" is commercial. Tagging a promotion as `INFORMATIVE` overrides the customer’s choice: don’t. ::: ### Why it matters eldo wallet filters recipients **at send time**, based on the category you set. It doesn't read the message. A promotion sent as `INFORMATIVE` therefore also reaches customers who **opted out** of marketing. What it costs you: - **Legal.** In France and the European Union, electronic direct marketing relies on the person's consent (GDPR, CNIL rules). Messaging customers who opted out exposes you to complaints and penalties. Their opt-out is dated and kept: records it. - **Losing the channel.** An annoyed customer turns off the pass's notifications or removes it from their wallet. You then also lose the useful messages: order ready, delay, card renewal. The `pass.uninstalled` webhook tells you, too late. - **Trust.** A "service" notification hiding an ad teaches customers to ignore all your notifications, including the important ones. - **Skewed analytics.** Your delivery statistics per category stop meaning anything, so you can't tell what works. When in doubt, use `COMMERCIAL`: you reach fewer customers, but all of them agreed to hear from you. ## Trip notifications For transit tickets, target **a trip**, not every holder. sends a message to every ticket sharing a `tripNumber`. See [Update a trip](/kb/guides/update-a-trip). ## Automatic notifications Some events notify without any call from you: new card, balance update, new coupon, new event… Rules are set per notification type and per channel (email, SMS, notification) in `communicationRules` (). Customise the wording with notification templates (). ## Track and manage - : delivery of one notification. - , : history. - , : edit or cancel a scheduled notification. ## Best practices - **One message, one action.** "Your order is ready, until Saturday 7 pm" beats a paragraph. - **Target.** A push sent to everyone about something that concerns 10% of holders gets the card deleted. - **Mind the clock.** Schedule with `SCHEDULED` rather than sending at 11 pm. - **Space out scheduled sends.** Two notifications scheduled too close together are rejected with `PUSH_NOTIFICATION_SCHEDULE_CONFLICT` (409). - **Use a future date.** A date in the past returns `SCHEDUle_DATE_PASSED`. :::note[iOS behaviour] On iPhone, the notification shows because a pass field changes value. Two sends with exactly the same text in a row may show nothing the second time. ::: ## Go further - [Apple Wallet vs Google Wallet](/kb/concepts/apple-vs-google-wallet) - [Locations](/kb/concepts/locations): a notification without sending, when the customer walks by - [Pass not updating](/kb/troubleshooting/pass-not-updating) ### Common API errors Source: https://developers.eldowallet.fr/en/kb/troubleshooting/common-api-errors # Common API errors The full list of codes is in the [error reference](/docs/errors). Here are the cases that come up most, and how to fix them. Every error has the same shape: a readable `message` (in the language of the `x-lang` header), an `errorCode` formatted `STATUS:CODE` (e.g. `404:MODEL_NOT_FOUND`), the `path` called and the `date`. ## Number one: JSON sent to a multipart endpoint Creates and updates that accept an image expect **`multipart/form-data`**: | Endpoint | Content-Type | |---|---| | , , , | `multipart/form-data` | | , , | `multipart/form-data` | | , | `multipart/form-data` | | Customers, passes, notifications, trips | `application/json` | Related errors: `INVALID_MULTIPART` (missing or malformed boundary), `UNSUPPORTED_MEDIA_TYPE` (415), `FILE_REQUIRED` (required image missing). Three multipart rules: 1. **Let the tool write the header.** With `curl -F` or a JavaScript `FormData`, the `Content-Type` and its `boundary` are added for you. A hand-written header without a boundary breaks the request. 2. **Nested objects as JSON strings.** `colors`, `security`, `locations`, `displayOptions`, `iosHeader`, `date`: one form field holding JSON. 3. **Booleans as text**: `"true"` / `"false"`. Images PNG or JPEG, 1 KB to 5 MB. ## 401: not authenticated - `Authorization: Bearer $ELDO_TOKEN` header missing or malformed: `UNAUTHORIZED`. - Token expired: log in again (), or use a wallet token for server integrations (). See [Authentication](/docs/authentication). ## 403: forbidden You are authenticated, but the resource is not yours (template or customer from another wallet), or your role is not enough: `FORBIDDEN`. A customer from another wallet returns `CLIENT_DOES_NOT_BELONG_TO_THIS_WALLET`. ## 404: not found - `MODEL_NOT_FOUND`: unknown id. Ids are ObjectIds: **24 hexadecimal characters**. - `PATH_NOT_FOUND`: unknown URL. Check the base URL (`https://api-v2.test.eldowallet.fr` in test, see [Environments](/docs/environments)) and the path. - `PASS_NOT_FOUND`: the scanned barcode matches no pass on that template. - `STORECARD_NOT_FOUND`: the wallet’s loyalty card is not configured yet (). ## 409: conflict - `EMAIL_ALREADY_EXISTS`, `CLIENT_ALREADY_EXISTS`: this customer already exists in the wallet. Update instead of recreating. - `BARCODE_ALREADY_EXISTS`: this barcode is already taken. - `OVERLAPPING_DATES`: the hotel room is already booked on those dates. - `PUSH_NOTIFICATION_SCHEDULE_CONFLICT`: a notification is already scheduled too close to that time. - `STORE_CARD_ALREADY_EXISTS`: a wallet has only one loyalty card. Edit it with . ## 400: bad request - `VALIDATION_ERROR`, `MISSING_REQUIRED_FIELDS`: required field missing or value not in the list. The details name the fields. - `UNEXPECTED_FIELD`: an unknown field was sent. Check spelling, e.g. `barcodeFormat` (coupon, transit) vs `barcodeType` (event, loyalty card). - `INVALID_JSON`: malformed JSON body. - `PASS_ALREADY_REDEEMED`: second on the same pass. If the first scan was a mistake, cancel with . - `PASS_NOT_REDEEMED`: on a pass never redeemed. - `SYNC_EXECUTION_NOT_ALLOWED`: `forceSync: true` on a bulk operation of 1,000 items or more. ## 5xx: server or external service error - `500` (`UNKNOWN_ERROR`): retry. If it persists, [contact support](#support) with the `path`, the `date` and the body sent. - `502`: the SMS provider failed (`SMS_SEND_FAILED`…). Try again later. - `503` (`PASS_BUILDER_ERROR`): the pass generation service is down. Try again later. ## Go further - [Error reference](/docs/errors) - [Authentication](/docs/authentication) - [Files and images](/docs/files-and-images) ### Email or SMS not received Source: https://developers.eldowallet.fr/en/kb/troubleshooting/email-not-received # Email or SMS not received The customer says nothing arrived. Before resending, check these points. ## 1. Was a send requested? eldo wallet only sends a message when you ask it to: | Call | Send flag | |---|---| | , | `sendCard` | | | `sendNotificationsToCustomers` | | , | `enableSendEmailOrSms` | Flag set to `false`? That is by design: passing the link on is up to you. ## 2. Do the communication rules allow it? - The wallet channel (`communicationChannel`: `email`, `sms` or `all`) and the rules per message type (`communicationRules`) are readable with . - The customer may have opted out of a channel: check their preferences with , and the history with . ## 3. Are the contact details right? - Typo in the email, non-existent domain. - Phone without international prefix: use `+33…`. - Check the record with , fix it with . ## 4. Is SMS configured and topped up? | Error | Cause | |---|---| | `SMS_CREDENTIALS_NOT_CONFIGURED`, `SMS_CONFIG_REQUIRED` | No SMS provider configured on the wallet. | | `INSUFFICIENT_CREDIT` | Not enough credits for the send. | | `INSUFFICIENT_CREDIT_PARTIAL` (409) | Some channels lack credits: confirm to send only on channels with credits. | | `SMS_LOGIN_FAILED`, `SMS_SEND_FAILED` (502) | The SMS provider failed. Try again later. | ## 5. On the customer side - **Spam and "Promotions" tabs**: ask the customer to check. - **Corporate filters**: work addresses sometimes block links. - **Filtered SMS**: some phones file texts from unknown senders separately. ## 6. Send a reminder resends the link to customers who have not installed their card: everyone (`all`) or a list (`specific`). ## Go further - [General FAQ](/kb/faq/general) - [Import a customer base](/kb/guides/import-customers-csv) ### Pass not updating Source: https://developers.eldowallet.fr/en/kb/troubleshooting/pass-not-updating # Pass not updating You changed some data. The API call succeeded. But the customer still sees the old value. Go through these checks in order. ```mermaid flowchart TD A[The pass shows the old value] --> B{Did the API call succeed?} B -- No --> B1[Fix the error: see Common API errors] B -- Yes --> C{Is the pass installed on the phone?} C -- No --> C1[The customer must add the pass first] C -- Yes --> D{Did you change the right object?} D -- No --> D1[Template or customer pass: see the table] D -- Yes --> E{Does the phone have network and updates on?} E -- No --> E1[Update arrives on next connection] E -- Yes --> F[Contact support with the customer id and the call time] ``` Still stuck? [Contact support](#support) with the customer id and the time of the call. ## 1. Did the call really succeed? Check the HTTP status and the response body. - `NOTHING_TO_UPDATE` (204): the values sent match the current ones. Nothing goes out. - `INVALID_MULTIPART` or `UNSUPPORTED_MEDIA_TYPE`: wrong Content-Type. See [Common API errors](/kb/troubleshooting/common-api-errors). - Bulk operation: the immediate response only says the operation was accepted. Check its result with . ## 2. Is the pass installed? A pass sent but never added to the Wallet gets no updates: it does not exist on the phone yet. Check the customer’s status with or , or listen for the `pass.installed` webhook. If they have not installed it, nudge them with . The customer may also have **removed** the pass (`pass.uninstalled` webhook). They need to add it again from their link. ## 3. Did you change the right object? | You want to change… | The right call | |---|---| | A customer’s balance (points, cashback) | , or from the till | | A customer’s name, email, type | | | The strip or details for one customer | | | The loyalty card design for everyone | | | A trip’s times | | | One passenger’s ticket | | | A customer’s coupon | | | A hotel booking | | ## 4. Is the pass in a final state? A cancelled or expired pass no longer updates: `PASS_ALREADY_CANCELED`, `PASS_ALREADY_EXPIRED`. ## 5. On the phone - **No network**: the update arrives on the next connection. - **Automatic updates off**: on iPhone, the customer can turn off updates for a pass in its details. They can also pull down on the back of the pass to force a refresh. - **Delay**: updates go through Apple's or Google's servers. They are not always instant. ## 6. The value changed, but no notification? That is expected in some cases: an update only shows a notification if the changed field is set to notify, and if the matching notification rule is on (`communicationRules`, ). See [Push notifications](/kb/push-notifications). ## Go further - [Pass lifecycle](/kb/concepts/pass-lifecycle) - [Apple Wallet vs Google Wallet](/kb/concepts/apple-vs-google-wallet) ## API reference The complete API contract (every endpoint, parameter, schema and error) is the OpenAPI file: https://developers.eldowallet.fr/en/openapi.en.yaml