> For the complete documentation index, see [llms.txt](https://docs.thewalletcrew.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thewalletcrew.io/developers-guides/fr/integration-guides/wallet/batch-operations.md).

# Opérations par lots

Mettez à jour des Cartes en masse à l’aide de l’API de mise à jour push ou d’imports de fichiers plats SFTP.

## Opérations par lot

Wallet Crew prend en charge deux mécanismes pour mettre à jour des cartes en masse : l’API de mise à jour par push et les imports de fichiers plats SFTP.

#### API de mise à jour par push

L’opération ci-dessous déclenche une mise à jour par push sur toutes les cartes correspondant à une condition de filtre. Il s’agit du mécanisme principal de traitement par lot pour les systèmes connectés à l’API.

Le point de terminaison accepte le même `filtre` tableau que le point de terminaison de liste des cartes. Voir les opérateurs de filtre et de requête pour la référence complète des filtres.

## Update passes in bulk

> Applies one change to every pass matching the filters (same fields and operators as \`GET /passes\`; no filter selects every pass) and pushes each to its holder's devices. Each pass is queued as its own update, spread by \`options.throughput\`: a 200 means scheduled, not done. Follow progress with \`GET /passes/pushUpdate/status\` under \`options.correlationId\`, stop it with \`POST /passes/pushUpdate/{correlationId}/cancel\`. \`additionalData\` is merged and \`passType\` replaces the template when sent; \`themeId\` omitted keeps each pass's theme, an empty string removes it. Identifiers are not changed.\
> \`\`\`\
> POST /api/{tenantId}/passes/pushUpdate?filter\[0].field=passType\&filter\[0].operator=equals\&filter\[0].value=loyalty\
> { "additionalData": { "offer": "summer2025" }, "options": { "throughput": 12, "correlationId": "campaign-summer-2025" } }\
> \`\`\`

````json
{"openapi":"3.1.1","info":{"title":"Neostore internal API","version":"v1"},"tags":[{"name":"Pass"}],"servers":[{"url":"https://app.neostore.cloud","description":"Production Server"},{"url":"https://app-qa.neostore.cloud","description":"Staging Server"}],"security":[{"admin-bearer":["tenant.pass:pushUpdate"]},{"apiKey":[]}],"components":{"securitySchemes":{"admin-bearer":{"type":"oauth2","flows":{"implicit":{"authorizationUrl":"https://auth.neostore.cloud/authorize?audience=https://app.neostore.cloud/api/","scopes":{}}}},"apiKey":{"type":"apiKey","name":"X-API-KEY","in":"header"}},"schemas":{"FilterModel":{"type":"object","properties":{"field":{"type":"string","description":"`id`, `passType`, `themeId`, `creationDate` and `lastUpdateDate` (UTC), `installationStatus` (installed wallets concatenated: `\"\"`, `\"apple\"`, `\"google\"` or `\"applegoogle\"`; test with `contains`), `identifiers.{key}`, or `metadata.{key}` (compared as the type the tenant's metadata configuration gives it)."},"operator":{"description":"Comparison operator. Required.","$ref":"#/components/schemas/FilterModelOperator"},"value":{"type":["null","array"],"items":{"type":"string"},"description":"Values to compare, as repeated `value` query parameters (several only for `in`/`notIn`). Numbers use `.` as decimal separator, booleans are `true`/`false`, dates are ISO 8601 with timezone.\nA value containing `{{`, `{%` or `{#` is first rendered as a Liquid template (`{{ 'now' | date: '%Y-%m-%d' }}`).\n<example>```\nGET /passes?filter[0].field=passType&filter[0].operator=in&filter[0].value=boarding&filter[0].value=loyalty\n```</example>"}},"additionalProperties":false,"description":"One condition on passes. Several conditions must all hold."},"FilterModelOperator":{"enum":["Contains","StartsWith","EndsWith","Equals","IsEmpty","IsNotEmpty","NotEquals","In","NotIn","GreaterThan","LessThan","GreaterThanOrEqual","LessThanOrEqual"],"type":"string","description":"How a pass filter compares the field with its value."},"MultipleUpdatePassData":{"type":"object","allOf":[{"description":"Changes to apply to an existing pass, with options.","$ref":"#/components/schemas/UpdatePassData_MultipleUpdatePassDataOptions"}],"additionalProperties":false,"description":"Changes applied to every matched pass. Omitted fields are left as they are."},"UpdatePassData_MultipleUpdatePassDataOptions":{"type":"object","properties":{"identifiers":{"type":"object","additionalProperties":{"type":"string"},"description":"Identifiers of the holder in other systems, keyed by system and field (`shopify.customerId`), matched by `findPass`, `id.{key}` lookups and filters. Avoid keys starting with `id.`."},"additionalData":{"type":["null","object"],"additionalProperties":{},"description":"Free data stored with the pass and available to its templates."},"themeId":{"type":["null","string"],"description":"Theme applied to the pass. Omit or `null` to leave it unchanged (none on create); an empty string removes it. An unknown id is refused with a 400."},"passType":{"type":["null","string"],"description":"Pass template to move the pass to. Omitted or blank keeps the current one; an unknown name is refused with a 400."},"updateMetadata":{"type":"boolean","description":"Replaced by `options.updateMetadata`.","default":false,"deprecated":true},"options":{"description":"How the update is applied. Omitted, the defaults of each option apply.","$ref":"#/components/schemas/MultipleUpdatePassDataOptions"}},"additionalProperties":false,"description":"Changes to apply to an existing pass, with options."},"MultipleUpdatePassDataOptions":{"type":"object","allOf":[{"$ref":"#/components/schemas/UpdatePassDataOptions"}],"properties":{"throughput":{"type":"number","description":"Passes updated per second, greater than 0 and at most 1000. Default 12; best effort.","format":"float"}},"additionalProperties":false,"description":"Options for a bulk update."},"UpdatePassDataOptions":{"type":"object","properties":{"updateMetadata":{"type":"boolean","description":"Recomputes the stored pass metadata read by filters and sorts. Slower; unneeded when only displayed content changes. Default `true`, except on a bulk update sent without a body."},"correlationId":{"type":["null","string"],"description":"Groups updates into one operation tracked by `GET /passes/pushUpdate/status` and stopped by `POST /passes/pushUpdate/{correlationId}/cancel`. Random when omitted; a synchronous update then records no progress."}},"additionalProperties":false,"description":"How an update is applied."},"PushUpdateResult":{"type":"object","properties":{"passCount":{"type":"integer","description":"Passes matched by the filters and queued for the update; 0 when none matched.","format":"int32"}},"additionalProperties":false,"description":"What a bulk update scheduled."},"ProblemDetails":{"type":"object","properties":{"type":{"type":["null","string"]},"title":{"type":["null","string"]},"status":{"type":["null","integer"],"format":"int32"},"detail":{"type":["null","string"]},"instance":{"type":["null","string"]}},"additionalProperties":{}},"HttpValidationProblemDetails":{"type":"object","allOf":[{"$ref":"#/components/schemas/ProblemDetails"}],"properties":{"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}},"additionalProperties":{}}}},"paths":{"/api/{tenantId}/passes/pushUpdate":{"post":{"tags":["Pass"],"summary":"Update passes in bulk","description":"Applies one change to every pass matching the filters (same fields and operators as `GET /passes`; no filter selects every pass) and pushes each to its holder's devices. Each pass is queued as its own update, spread by `options.throughput`: a 200 means scheduled, not done. Follow progress with `GET /passes/pushUpdate/status` under `options.correlationId`, stop it with `POST /passes/pushUpdate/{correlationId}/cancel`. `additionalData` is merged and `passType` replaces the template when sent; `themeId` omitted keeps each pass's theme, an empty string removes it. Identifiers are not changed.\n```\nPOST /api/{tenantId}/passes/pushUpdate?filter[0].field=passType&filter[0].operator=equals&filter[0].value=loyalty\n{ \"additionalData\": { \"offer\": \"summer2025\" }, \"options\": { \"throughput\": 12, \"correlationId\": \"campaign-summer-2025\" } }\n```","parameters":[{"name":"filter","in":"query","description":"Query `filter[i].field`, `filter[i].operator`, `filter[i].value`: conditions a pass must all meet.","schema":{"type":"array","items":{"description":"One condition on passes. Several conditions must all hold.","$ref":"#/components/schemas/FilterModel"}}},{"name":"tenantId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"description":"The change applied to each pass, and options. Omitted, each pass is pushed as it is, without recomputing metadata, at the tenant's default throughput.","content":{"application/json":{"schema":{"description":"Changes applied to every matched pass. Omitted fields are left as they are.","$ref":"#/components/schemas/MultipleUpdatePassData"}},"text/json":{"schema":{"description":"Changes applied to every matched pass. Omitted fields are left as they are.","$ref":"#/components/schemas/MultipleUpdatePassData"}},"application/*+json":{"schema":{"description":"Changes applied to every matched pass. Omitted fields are left as they are.","$ref":"#/components/schemas/MultipleUpdatePassData"}}}},"responses":{"200":{"description":"The passes are scheduled; the body gives how many.","content":{"text/plain":{"schema":{"description":"What a bulk update scheduled.","$ref":"#/components/schemas/PushUpdateResult"}},"application/json":{"schema":{"description":"What a bulk update scheduled.","$ref":"#/components/schemas/PushUpdateResult"}},"text/json":{"schema":{"description":"What a bulk update scheduled.","$ref":"#/components/schemas/PushUpdateResult"}}}},"400":{"description":"`passType` or `themeId` names a template or theme the tenant does not have, a filter names an unsupported field, or a filter has no operator.","content":{"text/plain":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}},"text/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"401":{"description":"The caller is not authenticated.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"403":{"description":"The caller lacks the <c>tenant.pass:pushUpdate</c> scope, or the endpoint is not enabled for the tenant.","content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}},"429":{"description":"Over 1200 requests per minute to this endpoint for the tenant (default limit); retry after <c>Retry-After</c>.","headers":{"Retry-After":{"description":"Seconds to wait before retrying. Sent when the limiter knows it.","schema":{"type":"integer"}}},"content":{"application/problem+json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/ProblemDetails"},{"$ref":"#/components/schemas/HttpValidationProblemDetails"}]}}}}}}}}}
````

Chaque carte correspondante est mise en file d’attente pour mise à jour indépendamment. La plateforme résout tous les identifiants de cartes correspondants dans la requête, puis les place en file d’attente comme une seule commande contenant N tâches individuelles par carte. Le `200` réponse signifie que le lot a été accepté et planifié — pas qu’il est terminé.

Il n’y a pas de limite stricte au nombre de cartes qu’un seul appel peut correspondre. `bypassQueue` n’est pas disponible sur ce point de terminaison.

{% hint style="info" %}
Vous pouvez fournir un `correlationId` dans le corps de la requête. La plateforme l’attache à chaque tâche mise en file d’attente, ce qui vous permet de suivre l’avancement du traitement via le point de terminaison des statistiques. Il n’existe pas de webhook de fin au niveau du lot.
{% endhint %}

**Échecs partiels**

Chaque carte est traitée indépendamment. Un échec sur une carte — par exemple une erreur de connecteur ou une carte introuvable — n’affecte pas les autres cartes du lot. Les cartes en échec ne sont pas relancées automatiquement, sauf si l’erreur sous-jacente est transitoire.

#### Imports de fichiers plats SFTP

Les imports SFTP vous permettent de mettre à jour des données supplémentaires, des identifiants, des modèles de carte et des métadonnées sur de nombreuses cartes en téléversant un fichier CSV. Cette méthode est conçue pour les systèmes hérités qui ne peuvent pas appeler l’API directement.

{% hint style="warning" %}
Les imports SFTP constituent une solution de dernier recours. Utilisez l’API de mise à jour par push pour tout système prenant en charge les appels HTTP. SFTP est une fonctionnalité optionnelle — contactez Wallet Crew pour l’activer sur votre tenant.
{% endhint %}

Chaque ligne du CSV cible une carte unique. Les cartes sont mises à jour de manière asynchrone après la détection du fichier sur le serveur SFTP. Comme pour l’API de mise à jour par push, les opérations sont effectuées par carte et indépendantes.

Pour le format complet du fichier, la référence des colonnes, les identifiants SFTP et les scripts de mappage personnalisés, voir [Mettre à jour une carte à l’aide de fichiers plats](/configure/fr/advanced-configuration/wallet/import-and-export/update-pass-using-flat-files.md) dans l’espace Configuration.

#### Choisir entre les deux approches

|                       | API de mise à jour par push                                     | fichier plat SFTP                                                                  |
| --------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
| Idéal pour            | Intégrations en temps réel ou planifiées avec accès à l’API     | Systèmes hérités avec exportations de fichiers plats uniquement                    |
| Ciblage               | Conditions de filtre (n’importe quel champ de données de carte) | Identifiant par ligne (liste explicite)                                            |
| Données mises à jour  | Déclenche une nouvelle récupération du connecteur via push      | Met à jour `additionalData`, `identifiants`, `passType`, `métadonnées` directement |
| Configuration         | Aucune configuration supplémentaire requise                     | Fonctionnalité optionnelle — doit être activée par tenant                          |
| Suivi de l’avancement | `correlationId` + point de terminaison des statistiques         | Envoi sans suivi                                                                   |

#### Suivi de l’avancement des lots dans la console

La console d’administration fournit une vue en temps réel de toutes les opérations par lot. Ouvrez **Paramètres → Surveillance → Mises à jour Wallet**.

La page affiche toutes les opérations de mise à jour par push avec les colonnes suivantes :

| Colonne           | Description                                   |
| ----------------- | --------------------------------------------- |
| Début             | Quand le lot a été mis en file d’attente      |
| Dernière activité | Événement de traitement le plus récent        |
| Opération         | Nom interne de l’opération                    |
| Corrélation       | Le `correlationId` fourni dans la requête API |
| Cartes            | Nombre total de cartes dans le lot            |
| Terminées         | Cartes traitées avec succès                   |
| Erreurs           | Cartes dont le traitement a échoué            |
| Apple             | Livraisons Apple Wallet                       |
| Google            | Livraisons Google Wallet                      |
| Statut            | Statut global du lot, tel que `Terminées`     |

Pour faire correspondre un appel API à une ligne, fournissez un `correlationId` dans l’opération de mise à jour par push. La valeur apparaît dans la **Corrélation** colonne, ce qui facilite la recherche du lot parmi les opérations concurrentes.

{% hint style="info" %}
Les opérations en cours peuvent être annulées depuis cette page. Il n’existe pas de webhook de fin au niveau du lot. Utilisez cette vue pour la surveillance opérationnelle.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.thewalletcrew.io/developers-guides/fr/integration-guides/wallet/batch-operations.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
