Skip to content

Enregistrer Webhook

Endpoints pour créer, lister et supprimer des webhooks de notification.


Créer un webhook

POST /api/external/webhooks

Headers

HeaderTypeObligatoireDescription
AuthorizationStringOuiApiKey {client_id}:{client_secret}
Content-TypeStringOuiapplication/json
hmacStringOuiSignature HMAC-SHA512 du body (en savoir plus)

Request Body

ChampTypeObligatoireDéfautDescription
urlStringOui--URL pour recevoir les notifications (HTTPS par défaut)
eventsArrayOui--Liste des événements à souscrire. Doit être un array non-vide avec au moins un événement valide du tableau ci-dessous. Omettre le champ retourne 400 {"errors": {"events": ["can't be blank"]}}.
secretStringNonauto-généréClé pour signature HMAC-SHA256 des livraisons. Si omis, une valeur aléatoire est générée automatiquement.
descriptionStringNonnullDescription libre du webhook pour identification interne
allow_insecureBooleanNonfalsePermet les URLs HTTP (non-HTTPS). La sécurité des données est la responsabilité du client.

Événements disponibles (uniquement PIX — les autres produits ne sont pas dans le périmètre d'Owem aujourd'hui) :

ÉvénementStatus bodyDescriptionDéclenchement
pix.charge.createdcreatedQR code généré ou cash-in initiéActif
pix.charge.paidpaidPIX reçu et liquidéActif
pix.charge.expiredexpiredQR code expiré sans paiement (worker toutes les 5 min)Actif
pix.charge.cancelledcancelledQR code annulé avant le paiementEnregistré, pas encore déclenché
pix.payout.queuedqueuedPIX envoyé mis en file par rate limit (ClientLimiter par merchant OU bucket DICT BACEN)Actif
pix.payout.processingprocessingPIX envoyé, en attente de confirmationActif
pix.payout.confirmedsettledPIX envoyé et confirmé (terminal)Actif
pix.payout.failedrejectedPIX envoyé rejeté (terminal)Actif
pix.payout.returnedreturnedPIX envoyé rembourséActif
pix.refund.requestedrequestedDemande de remboursement reçue (infraction BACEN) ; blocage conservatoire créé sur le solde du clientActif
pix.refund.completedsettled / completedAnalyse de la défense finalisée et remboursement exécuté (ou libéré)Actif
pix.return.receivedsettledRemboursement PIX reçu (crédit)Actif
pix.infraction.createdACKNOWLEDGEDInfraction PIX signalée par la contrepartie via BACEN DICTActif
pix.infraction.resolvedCLOSED / CANCELLEDInfraction résolue (admin close, auto-deny ou contrepartie a annulé)Actif
pix.infraction.defense_submitteddefense_submittedDéfense soumise par le merchantActif
webhook.testtestTest manuel. Disponible uniquement via portail Admin/Merchant — il n'y a pas d'endpoint External API pour déclencherDéclenchement manuel

pix.charge.cancelled n'est pas encore déclenché

L'événement est dans l'enum des abonnements valides, mais aucun code en production ne déclenche de notifications de ce type aujourd'hui (flux d'annulation de QR code non implémenté). Vous pouvez l'inclure dans le tableau events — l'API l'accepte — mais aucune notification n'arrivera à votre endpoint tant que le flux ne sera pas implémenté.

Tout événement hors de ce tableau est rejeté

À la création d'un webhook, validate_events dans le backend vérifie chaque item contre l'enum @valid_events (schéma Fluxiq.Schemas.Webhooks.Webhook). Les événements inconnus (boleto.paid, account.created, sta.file.*, etc.) retournent 400 avec l'erreur events: contains invalid events: .... Avant la session 163, ces noms étaient dans l'enum comme aspirationnels — ils ont été supprimés car il n'y avait pas de dispatchers en code.

Payloads de chaque événement

Des exemples de payload complets pour chaque événement sont dans Payloads des webhooks.

Exemple

bash
BODY='{"url":"https://votresite.com.br/webhook","events":["pix.charge.paid","pix.payout.confirmed"]}'
HMAC=$(echo -n "$BODY" | openssl dgst -sha512 -hmac "$CLIENT_SECRET" | awk '{print $2}')

curl -X POST https://api.owem.com.br/api/external/webhooks \
  -H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET" \
  -H "Content-Type: application/json" \
  -H "hmac: $HMAC" \
  -d "$BODY"

Réponse de succès (201)

json
{
  "worked": true,
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "url": "https://votresite.com.br/webhook",
  "events": ["pix.charge.paid", "pix.payout.confirmed"],
  "secret": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
  "description": null,
  "is_active": true,
  "created_at": "2026-03-07T15:30:00Z"
}

Format du id

Le id du webhook est un UUID v4 canonique (36 caractères avec tirets). Utilisez cette valeur directement dans DELETE /api/external/webhooks/:id.

Réponse d'erreur (422)

json
{
  "worked": false,
  "detail": "URL deve utilizar HTTPS"
}

Uniquement HTTPS par défaut

L'URL du webhook doit utiliser HTTPS. Les URLs HTTP seront rejetées, sauf si allow_insecure: true est envoyé à l'enregistrement.

Important — Secret du webhook

Le champ secret retourné dans la réponse d'enregistrement est la clé utilisée pour signer les livraisons du webhook (HMAC-SHA256). Stockez cette valeur en sécurité dès réception — c'est elle qui valide qu'une notification vient réellement d'Owem Pay.

Ne confondez pas avec client_secret :

  • client_secret = authentification de vos requêtes à l'API (header Authorization)
  • secret du webhook = vérification de la signature des livraisons reçues (header X-Owem-Signature)

Si vous n'envoyez pas le champ secret à l'enregistrement, une valeur aléatoire sera générée automatiquement et retournée dans la réponse.

Voir Validation de webhooks pour des exemples de comment vérifier la signature.

Récupérer le secret plus tard

Le secret est retourné à la fois dans POST /api/external/webhooks (création) et dans GET /api/external/webhooks et GET /api/external/webhooks/:id (consultation). Si vous avez perdu la valeur, il suffit de consulter le webhook à nouveau via GET.

Dans une future version, ce comportement pourrait être restreint (afficher uniquement à la création) ; nous recommandons de stocker le secret dans un secret manager (vault, SSM, etc.) au moment de l'enregistrement.

URLs HTTP

Par défaut, les webhooks exigent HTTPS pour garantir la sécurité des données en transit. Pour utiliser HTTP, envoyez allow_insecure: true lors de l'enregistrement du webhook.

Attention

Les URLs HTTP transmettent les données sans chiffrement. La sécurité et la confidentialité des informations transitées sont sous l'entière responsabilité du client. Owem Pay effectue la livraison du webhook normalement, mais ne se responsabilise pas en cas d'interception ou de fuite de données sur des connexions non chiffrées.

Les URLs privées sont toujours bloquées

Même avec allow_insecure: true, les URLs pointant vers des adresses privées/internes sont rejetées :

  • localhost / 127.x.x.x
  • RFC1918 : 10.x.x.x, 192.168.x.x, 172.16-31.x.x
  • TLDs internes : .local, .internal

Les webhooks doivent pointer vers des URLs publiques accessibles sur internet.


Lister les webhooks

GET /api/external/webhooks

Headers

HeaderTypeObligatoireDescription
AuthorizationStringOuiApiKey {client_id}:{client_secret}

Exemple

bash
curl -X GET https://api.owem.com.br/api/external/webhooks \
  -H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET"

Réponse de succès (200)

Retourne un tableau d'objets (non enveloppé dans {"worked": true}). Chaque item contient les mêmes champs que l'enregistrement, y compris secret.

json
[
  {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "url": "https://votresite.com.br/webhook",
    "events": ["pix.charge.paid", "pix.payout.confirmed"],
    "description": null,
    "account_id": 10014,
    "is_active": true,
    "allow_insecure": false,
    "status": "active",
    "secret": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
    "created_at": "2026-03-07T15:30:00",
    "updated_at": "2026-03-07T15:30:00"
  }
]
ChampTypeDescription
idstring (UUID)Identifiant du webhook
urlstringURL de destination
eventsarrayÉvénements souscrits
descriptionstring ou nullDescription optionnelle
account_idinteger ou nullCompte associé. null = webhook global pour l'API key (si supporté)
is_activebooleanfalse = webhook en pause, aucune delivery n'est déclenchée
allow_insecurebooleantrue = URLs HTTP acceptées
statusstringDérivé de is_active"active" ou "inactive"
secretstringClé HMAC-SHA256 utilisée pour signer les livraisons. Retournée dans le LIST pour permettre la récupération si le client a perdu la valeur originale
created_at / updated_atstring ISO 8601Timestamps en UTC, format NaiveDateTime (sans suffixe Z, ex. : "2026-03-07T15:30:00"). Différent des autres champs de date dans les payloads de webhook (paid_at, returned_at, expired_at) qui utilisent DateTime ISO 8601 avec Z final. Supposez toujours UTC pour les champs de l'objet webhook

Supprimer un webhook

DELETE /api/external/webhooks/:id

Headers

HeaderTypeObligatoireDescription
AuthorizationStringOuiApiKey {client_id}:{client_secret}

Path Parameters

ParamètreTypeObligatoireDescription
idString (UUID)OuiID du webhook (UUID v4 retourné par l'endpoint de création)

Exemple

bash
curl -X DELETE https://api.owem.com.br/api/external/webhooks/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
  -H "Authorization: ApiKey $CLIENT_ID:$CLIENT_SECRET"

Réponse de succès (204)

HTTP 204 No Content — body vide. Le webhook a été supprimé avec succès ; aucune delivery en attente ne sera déclenchée.

Premier appel : 204. Suivants : 404

Le premier appel réussi retourne 204 No Content. Les appels suivants avec le même id retournent 404 { "errors": { "not_found": "webhook not found" } } — le webhook a déjà été supprimé. Ce n'est pas une idempotence stricte HTTP (où tout appel retournerait 204) — c'est le comportement standard de DELETE quand la ressource cesse d'exister. Écrivez votre intégration pour accepter à la fois 204 et 404 comme « le webhook n'est plus actif ».

Réponse d'erreur (400)

Format de id invalide (pas un UUID) :

json
{
  "errors": {
    "bad_request": "id must be a valid UUID"
  }
}

Réponse d'erreur (404)

Le webhook n'existe pas ou n'appartient pas à votre compte :

json
{
  "errors": {
    "not_found": "webhook not found"
  }
}

Owem Pay Instituição de Pagamento — ISPB 37839059