Home › Developer portal › Bank integration guide
Document menu← Home
Developer portal
  1. Quick start
  2. Authentication
  3. REST API reference
  4. Webhooks guide
  5. Exports guide
  6. ERP integration guide
  7. Bank integration guide
  8. Tauraco Collateral documentation
  9. MCP documentation
  10. Code samples
  11. Error handling
  12. Sandbox environments
  13. Versions and changelog
  14. FAQ
  15. Developer support
Developers

Bank integration guide

The bank API lets a financial institution read, from its own information system, the financing requests that companies sent it through Tauraco: frozen snapshot, timeline, documents, instalments, reconciliation and security interests. It takes no decision: offers, declines, disbursements, drawdowns, discharges and releases remain human actions of a decision-maker, in the bank portal.

Online since 2026-11-05 under https://api.tauraco.io/public/v1/bank (answers 401 without a token). The legal notices are validated. No real institution is connected yet: connecting an institution is a contractual step with Tauraco. The sandbox, built with a fictitious bank, is not launched yet.

Prerequisites

  1. The institution has a financer space in Tauraco.
  2. A decision-maker of the institution creates a "bank" client in Data & integrations › Institution API keys. The secret is shown only once.
  3. Tauraco gives this client a technical member with the LECTEUR role (read) or OPERATEUR (additional information and reconciliation), never DECIDEUR. Revoking the client suspends this member immediately.
  4. The client gets a client_credentials token with bank:* scopes and resource=https://api.tauraco.io/public/v1.

A company stays in control of its request: the institution only sees the requests addressed to it, and no longer sees the documents of a request it declined.

Operations

All of them live under https://api.tauraco.io/public/v1/bank.

OperationPathScope
Inbox of received requestsGET /bank/inboxbank:inbox:read
Request (frozen snapshot, timeline)GET /bank/requests/{request_id}bank:requests:read
Documents: list and streamed contentGET /bank/requests/{request_id}/documents, GET /bank/requests/{request_id}/documents/{document_id}/contentbank:documents:read
Requests for additional information: list, createGET and POST /bank/requests/{request_id}/information-requestsbank:requests:read (list), bank:info-requests:write (create)
Facilities and instalmentsGET /bank/facilities, GET /bank/facilities/{facility_id}, GET /bank/repaymentsbank:repayments:read
Reconciliation: create, exceptions, resolutionPOST /bank/facilities/{facility_id}/reconciliations, GET /bank/reconciliations/exceptions, POST /bank/reconciliations/{reconciliation_id}/resolvebank:reconciliation:write (create, resolve), bank:repayments:read (exceptions)
Collateral security interests: list (summarised alerts), detail, timelineGET /bank/collateral, GET /bank/collateral/{guarantee_id}, GET /bank/collateral/{guarantee_id}/eventsbank:collateral:read

Closing an additional-information request from the institution side does not exist in v1; the asset registry and the alert sheet are not exposed. Amounts are decimal strings in major units; the frozen request (snapshot) is served as it was frozen at submission, its amounts being integers in minor units.

Every request view and every document download is recorded in the Tauraco audit log. A document is streamed (25 MB at most), with Cache-Control: private, no-store, X-Content-Type-Options: nosniff and X-Content-SHA256.

A request withdrawn by the company stays readable, documents included, by a recipient institution (historical read, as in the portal); it stays refused to an institution that is not a recipient or that declined. A resource not addressed to your institution answers 404 not_found, indistinguishable from a missing resource.

What the API does not return

  • No internal amount and no offer of another institution.
  • No private note of Collateral files.
  • No credit decision: the API does not make offers, decline or disburse.

Every answer about a security interest carries an avertissement_juridique field: data recorded in Tauraco is not legal proof of an enforceable security interest. The text of this notice was validated before the bank API opened, on 2026-11-05.

Receive financing events

The financing.requested, financing.status_changed, financing.funded and contract.signed events are sent to the institution concerned, as are collateral.registered and collateral.released. They carry no amount and no rate: read the request again through data.links.self (…/public/v1/bank/requests/{id}). Declare the endpoint in Institution webhooks (decision-maker) and verify the signature as described in the webhooks guide.

python/recepteur_webhook_banque.pyDownload the file
"""Recevoir un webhook bancaire (événements ``financing.*``) et relire le dossier.

Receive a bank webhook (``financing.*`` events) and fetch the request.

L'événement est mince (identifiants, statut, horodatages ; aucun montant, aucun taux) :
on vérifie la signature, on dédoublonne par ``webhook-id``, puis on relit le dossier
par ``data.links.self`` (``…/public/v1/bank/requests/{id}``) avec un jeton du client
``bank`` (scopes ``bank:*``). La relecture applique les mêmes contrôles que le portail
banque : dossier adressé à l'institution, destinataire n'ayant pas décliné.

Le jeton n'est envoyé qu'à l'API Tauraco : un lien qui ne commence pas exactement par
``TAURACO_API_BASE + "/bank/requests/"`` est ignoré (un événement forgé ne doit pas
faire partir le jeton vers un autre hôte). Le secret du webhook est obligatoire.

Usage : TAURACO_WEBHOOK_SECRET=… TAURACO_CLIENT_ID=… TAURACO_CLIENT_SECRET=… \\
        python3 recepteur_webhook_banque.py
"""
from __future__ import annotations

import json
import os
import re
from http.server import BaseHTTPRequestHandler, HTTPServer
from typing import Optional

from client_credentials import API_BASE, TauracoError, get_token, http_transport
from verifier_webhook import AlreadySeen, secret_key, verify_signature

FINANCING_EVENTS = {"financing.requested", "financing.status_changed", "financing.funded", "contract.signed"}
#: Seuls liens suivis : l'API Tauraco configurée, ressource ``/bank/requests/{id}``.
REQUEST_PREFIX = API_BASE.rstrip("/") + "/bank/requests/"
_REQUEST_ID = re.compile(r"[A-Za-z0-9_-]{1,128}")


def request_link(event: dict) -> Optional[str]:
    """Lien de relecture du dossier, ou ``None`` (événement hors financement, lien absent)."""
    if event.get("type") not in FINANCING_EVENTS:
        return None
    links = (event.get("data") or {}).get("links") or {}
    link = links.get("self")
    if not isinstance(link, str) or not link.startswith(REQUEST_PREFIX):
        return None  # autre hôte, autre chemin : jamais de jeton envoyé
    return link if _REQUEST_ID.fullmatch(link[len(REQUEST_PREFIX):]) else None


def fetch_request(link: str) -> dict:
    if request_link({"type": "financing.requested", "data": {"links": {"self": link}}}) != link:
        raise ValueError("lien de relecture hors de l'API Tauraco : refusé")
    token = get_token(os.environ["TAURACO_CLIENT_ID"], os.environ["TAURACO_CLIENT_SECRET"],
                      "bank:requests:read")["access_token"]
    status, _headers, raw = http_transport("GET", link, {"Authorization": f"Bearer {token}",
                                                          "Accept": "application/json"}, None)
    payload = json.loads(raw or b"{}")
    if status != 200:
        raise TauracoError(status, payload)
    return payload


def make_bank_handler(secret: str) -> type[BaseHTTPRequestHandler]:
    """Récepteur lié à ``secret`` ; ``ValueError`` si le secret est vide ou illisible."""
    if secret_key(secret) is None:
        raise ValueError("secret de webhook vide ou illisible (whsec_ + base64 attendu)")
    seen = AlreadySeen()

    class BankReceiver(BaseHTTPRequestHandler):
        def do_POST(self) -> None:  # noqa: N802
            body = self.rfile.read(int(self.headers.get("Content-Length") or 0))
            headers = dict(self.headers.items())
            if not verify_signature(secret, headers, body):
                self.send_response(401)
                self.end_headers()
                return
            self.send_response(204)
            self.end_headers()
            event = json.loads(body)
            if not seen.first_time(event["id"]):
                return  # reprise ou rejeu : déjà traité
            link = request_link(event)
            if link:
                dossier = fetch_request(link)
                print(event["type"], event["data"].get("request_id"), dossier.get("status"))

        def log_message(self, *_args) -> None:
            return

    return BankReceiver


if __name__ == "__main__":
    handler = make_bank_handler(os.environ["TAURACO_WEBHOOK_SECRET"])  # aucune valeur par défaut
    HTTPServer(("127.0.0.1", 8788), handler).serve_forever()

Interoperability

  • Transport security: HTTPS; mTLS (RFC 9440) documented but not implemented in v1, for lack of a dedicated host name.
  • Formats: JSON; ISO 20022 camt.053 (statements) developed, experimental and not validated; pain.001 (transfers) not supported in v1.
  • Ecosystem bonds and guarantees: outside the v1 bank API.