Skip to content

PIX Cash-Out Copier-Coller

Effectue un virement PIX en utilisant le code EMV (copier-coller) extrait d'un QR Code PIX.

Endpoint

POST /api/external/pix/cash-out

Headers

HeaderTypeObligatoireDescription
AuthorizationStringOuiApiKey {client_id}:{client_secret}
Content-TypeStringOuiapplication/json
hmacStringOuiSignature HMAC-SHA512 du body (hex)
Idempotency-KeyStringNonClé unique pour éviter le traitement en double (max 256 caractères). Comportement identique à celui documenté dans PIX Cash-Out par clé

Permission obligatoire

L'API Key doit avoir la permission transfer:write pour envoyer du PIX. Sans elle, la requête retourne 403 Forbidden. Voir comment configurer les permissions.

Request Body

ChampTypeObligatoireDescription
amountIntegerOuiValeur en centavos. R$ 30,00 = 3000. En QR dynamique, cette valeur est remplacée par la consultation de l'URL du QR — voir ci-dessous
emvStringOuiCode EMV copier-coller du QR Code PIX. L'alias codigo_copia_cola est également accepté mais emv est canonique — préférez-le
descriptionStringNonDescription du virement (max 140 caractères)
external_idStringNonIdentifiant de votre système pour le suivi. Max 128 caractères après trim. Uniquement caractères a-zA-Z0-9._:-. Retourné dans les réponses et webhooks. Les valeurs invalides sont silencieusement écartées (la transaction continue avec external_id: null). Voir PIX Cash-Out par clé pour les détails.
end_to_end_idStringNonEnd-to-End ID au format BACEN. Recommandé à omettre — le backend génère en déterministe. N'envoyez qu'en scénario de reprocessement coordonné

Code copier-coller

Le champ emv accepte le code EMV complet copié d'un QR Code PIX. L'API extrait automatiquement la clé PIX, les données du bénéficiaire et la valeur originale de la facturation depuis le payload EMV. Le backend valide le CRC-16/CCITT-FFFF et la structure TLV avant de continuer.

QR Statique vs QR Dynamique

L'API traite les deux types différemment :

QR Statique (PoIM=11, sans URL) :

  • La clé PIX et les métadonnées viennent directement du payload EMV
  • Le amount envoyé dans la requête est utilisé directement comme valeur du virement
  • Pas de consultation externe — liquidation immédiate après validation DICT

QR Dynamique (PoIM=12, avec URL dans le tag 26/25) :

  • Le backend fait Provider.consult_qrcode sur l'URL incorporée dans le payload EMV et reçoit un JWS (BACEN Manual 2.9.0)
  • La valeur autoritative vient de la réponse JWS (valor.original) et remplace le amount envoyé par le client
  • Si la consultation de l'URL échoue (timeout, JWS invalide, QR expiré côté émetteur), retourne HTTP 422 avec detail: "QR Code dinamico nao pode ser resolvido"
  • Utilisez la valeur remplacée comme source de vérité — net_amount dans la réponse reflète la valeur réellement prélevée

Ne vous fiez pas à la valeur envoyée en QR dynamique

En QR dynamique, consultez toujours la réponse du POST pour obtenir l'amount effectif. Le backend ignore toute divergence entre la valeur envoyée et la valeur retournée par l'émetteur du QR — c'est une exigence BACEN.

Valeurs monétaires

Les valeurs d'entrée sont en centavos (R$ 1,00 = 100). Les valeurs de réponse sont en unités de base (R$ 1,00 = 10000). Pour convertir la réponse en reais, divisez par 10 000. N'utilisez jamais de virgule flottante.

Exemple

bash
curl -X POST https://api.owem.com.br/api/external/pix/cash-out \
  -H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "hmac: $HMAC" \
  -d '{
    "amount": 3000,
    "emv": "00020126580014br.gov.bcb.pix0136a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d5204000053039865802BR5913NOME RECEBEDOR6008BRASILIA62070503***6304ABCD",
    "description": "Paiement via QR Code",
    "external_id": "invoice-4521"
  }'

Réponse de succès -- 200 / 202

json
{
  "worked": true,
  "final": false,
  "transaction_id": "PIXOUT20260309a1b2c3d4e5f6",
  "end_to_end_id": "E37839059202603091530abcdef01",
  "external_id": "invoice-4521",
  "amount": 300000,
  "fee_amount": 350,
  "net_amount": 300350,
  "status": "accepted",
  "detail": "PIX enviado para processamento"
}

HTTP 200 vs 202

  • HTTP 200 : Transaction déjà liquidée (final: true, status: "settled").
  • HTTP 202 : Transaction acceptée pour traitement (final: false). status peut être "accepted" (flux normal), "queued" (rate-limit DICT — retry automatique toutes les 3s pendant jusqu'à 120 min, session 155) ou "pending_approval" (en attente d'approbation). Suivez le status via polling ou webhook.
ChampTypeDescription
workedBooleantrue indique que la requête a été acceptée
finalBooleantrue quand la transaction a atteint un état terminal (liquidée ou rejetée). false quand encore en traitement
transaction_idStringIdentifiant unique de la transaction
end_to_end_idStringIdentifiant End-to-End dans le SPI/BACEN
external_idStringVotre identifiant, retourné tel qu'envoyé. null s'il n'a pas été renseigné
amountIntegerValeur du virement en unités de base (÷ 10 000 pour reais). 300000 = R$ 30,00. En QR dynamique, reflète la valeur autoritative retournée par la consultation JWS — peut différer du amount envoyé dans le request
fee_amountIntegerFrais prélevés en unités de base (÷ 10 000 pour reais)
net_amountIntegerValeur brute débitée sur le compte payeur (amount + fee_amount) — ce n'est pas la valeur que le destinataire reçoit. Voir la note dans PIX Cash-Out par clé sur l'asymétrie de sémantique entre cash-out et cash-in
statusStringUn de : accepted, settled, queued, pending_approval. Voir détails dans PIX Cash-Out par clé
detailStringMessage descriptif

Codes de rejet

Les rejets du cash-out par EMV suivent exactement le même pattern que PIX Cash-Out par clé — deux formats de body (Format A {status: "failed", errors: [...]} et Format B {errors: {bad_request: "..."}}), trois classes d'erreur (validation / intégration / rate-limit en file), et les mêmes vocabulaires UPPERCASE × lowercase. Les items ci-dessous ne mettent en évidence que les codes spécifiques au chemin EMV.

Erreurs de validation spécifiques à l'EMV (HTTP 400 / 422)

HTTPFormatChamp avec codeSignification
400Berrors.bad_request: "invalid emv payload"Payload EMV mal formé (CRC-16 invalide, TLV incomplet, tag obligatoire absent) — détecté par le parser avant l'Orchestrator
400Berrors.bad_request: "invalid or missing amount"amount absent, zéro, négatif ou non-entier (QR statique)
422Aerrors[0].code: "same_institution_transfer"QR généré par un compte Owem — PIX intra-institutionnel non supporté (utilisez TEF). Note : HTTP 422 (pas 400), avec shape {status: "failed", errors: [{code: "same_institution_transfer", params: []}]}
422detail: "QR Code dinamico nao pode ser resolvido"Échec de la consultation de l'URL du QR dynamique (timeout, JWS invalide, QR expiré côté émetteur) — ce chemin est traité par le controller avant l'Orchestrator, retourne le shape legacy {errors: {unprocessable_entity: "..."}}
422Aerrors[0].code: "insufficient_balance"Solde disponible inférieur à amount + fee_amount

Autres erreurs

Pour les codes dict_key_not_found, dict_key_blocked, dict_rate_limited (synchrone), dict_bucket_exhausted (synchrone), dict_lookup_failed, provider_rejected, provider_schema_error, provider_unknown_errortous retournent HTTP 400 au Format A (pas 429 ni 503). Détails dans PIX Cash-Out par clé — Erreurs d'intégration.

Rate-limit avec retry automatique (HTTP 202 queued)

Quand le ClientLimiter per-merchant (default DICT_CLIENT_MAX_PER_MIN=120) ou le DictBucket.Guard global (refill DICT_BUCKET_REFILL_RATE=18/min, capacity 250) détecte que la limite a été atteinte avant d'arriver à OnZ, le cash-out est mis en file dans Fluxiq.Workers.PixOutRetryWorker et retourne HTTP 202 avec status: "queued". Le flag pix_out_retry_queue_enabled est ON en PRD depuis session 155. Détails complets dans PIX Cash-Out par clé — Rate-limit avec retry automatique.

Rejets BACEN asynchrones

Après acceptation initiale (HTTP 202), le SPI/BACEN peut rejeter via PACS.002 RJCT. Ce rejet :

  • Ne modifie pas la réponse HTTP originale (déjà envoyée en 202)
  • Apparaît via GET /transactions/:id avec status: "failed" + reason_code (AC03, AB03, ED05 etc.) + reason_description en anglais
  • Déclenche le webhook pix.payout.rejected avec les mêmes champs structurés

Voir la liste complète des codes BACEN dans Consulter Cash-Out par ID.

Étapes suivantes

Après avoir créé le virement, suivez le status via :

Ou recevez la confirmation automatiquement via Webhook.

Owem Pay Instituição de Pagamento — ISPB 37839059