Skip to content

BACEN Integration

This guide covers how FluxiQ PIX integrates with Banco Central do Brasil (BACEN) for participation in the PIX instant payment system.

Overview

PIX is Brazil's instant payment system operated by BACEN. Direct participants must integrate with two primary APIs:

  • DICT (Diretorio de Identificadores de Contas Transacionais) - The PIX key directory
  • SPI (Sistema de Pagamentos Instantaneos) - The instant payment settlement system

Both APIs require mutual TLS (mTLS) authentication using ICP-Brasil digital certificates and operate within the RSFN (Rede do Sistema Financeiro Nacional) network.

BACEN Environments

EnvironmentDICT URLSPI URL
Homologationhttps://dict-h.pi.rsfn.net.br:16522/api/v2https://spi-h.pi.rsfn.net.br:16522
Productionhttps://dict.pi.rsfn.net.br:16522/api/v2https://spi.pi.rsfn.net.br:16522

WARNING

The homologation environment has rate limits and synthetic data. Do not use production credentials in homologation.

Certificate Requirements

ICP-Brasil Certificates

All communication with BACEN requires certificates issued by an ICP-Brasil accredited authority. The certificate must:

  1. Be a type A1 or A3 certificate
  2. Contain the institution's CNPJ in the Subject Alternative Name
  3. Be issued by an authority in the ICP-Brasil chain of trust
  4. Have the ISPB registered with BACEN for the PIX participant

Certificate Configuration

elixir
# config/runtime.exs
config :shared, Shared.Bacen.Client,
  cert_path: System.get_env("CERT_PATH", "./certs/client.pem"),
  key_path: System.get_env("KEY_PATH", "./certs/client_key.pem"),
  ca_path: System.get_env("CA_PATH", "./certs/ca_chain.pem"),
  bacen_ca_path: System.get_env("BACEN_CA_PATH", "./certs/bacen_ca.pem")

mTLS Handshake

The Shared BACEN client configures Erlang's :ssl module for mutual authentication:

elixir
defmodule Shared.Bacen.Client do
  def ssl_options do
    [
      certfile: config(:cert_path),
      keyfile: config(:key_path),
      cacertfile: config(:ca_path),
      verify: :verify_peer,
      depth: 4,
      versions: [:"tlsv1.2", :"tlsv1.3"],
      ciphers: :ssl.cipher_suites(:default, :"tlsv1.3")
    ]
  end
end

DICT Integration

The DICT API manages PIX keys that map user identifiers (CPF, CNPJ, phone, email, EVP) to bank account information.

Supported Operations

OperationHTTP MethodEndpointDescription
Create KeyPOST/api/v2/keyRegister a new PIX key
Delete KeyDELETE/api/v2/key/{key}Remove a registered key
Lookup KeyGET/api/v2/key/{key}Query key details
List KeysGET/api/v2/keyList keys by ISPB or owner
Create ClaimPOST/api/v2/claimInitiate key portability or ownership claim
Confirm ClaimPUT/api/v2/claim/{claimId}Accept or deny a claim
Create InfractionPOST/api/v2/infractionReport fraud or infraction
Create MEDPOST/api/v2/medSpecial return mechanism (MED 2.0)

Key Types

elixir
defmodule Shared.Schema.PixKey do
  @key_types [:cpf, :cnpj, :phone, :email, :evp]

  # CPF:   11-digit Brazilian individual tax ID
  # CNPJ:  14-digit Brazilian company tax ID
  # PHONE: +55DDDNNNNNNNNN format
  # EMAIL: Valid email address, max 77 characters
  # EVP:   UUID v4 random key (Endere\u00e7o Virtual de Pagamento)
end

DICT Synchronization

BACEN requires participants to maintain a local mirror of the DICT and perform periodic synchronization:

elixir
defmodule DictService.SyncWorker do
  use GenServer

  # Sync every 15 minutes as required by BACEN
  @sync_interval :timer.minutes(15)

  def handle_info(:sync, state) do
    {:ok, changes} = Shared.Bacen.Client.get("/api/v2/sync/#{state.last_sync_id}")
    process_changes(changes)
    schedule_sync()
    {:noreply, %{state | last_sync_id: changes.last_id}}
  end
end

SPI Integration

The SPI handles the actual movement of funds between participants.

Message Types (ISO 20022)

MessageTypeDirectionPurpose
pacs.008FIToFICstmrCdtTrfOutboundPayment initiation
pacs.002FIToFIPmtStsRptInboundPayment status report
pacs.004PmtRtrOutboundPayment return/reversal
camt.053BkToCstmrStmtInboundAccount statement
camt.060AcctRptgReqOutboundStatement request

XML Message Construction

SPI messages follow ISO 20022 XML format. The platform constructs and signs these messages:

elixir
defmodule SpiService.Message.Pacs008 do
  @moduledoc "Constructs pacs.008 payment initiation messages"

  def build(payment) do
    """
    <?xml version="1.0" encoding="UTF-8"?>
    <Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.09">
      <FIToFICstmrCdtTrf>
        <GrpHdr>
          <MsgId>#{payment.message_id}</MsgId>
          <CreDtTm>#{DateTime.to_iso8601(DateTime.utc_now())}</CreDtTm>
          <NbOfTxs>1</NbOfTxs>
          <SttlmInf>
            <SttlmMtd>CLRG</SttlmMtd>
          </SttlmInf>
        </GrpHdr>
        <CdtTrfTxInf>
          <PmtId>
            <EndToEndId>#{payment.end_to_end_id}</EndToEndId>
            <TxId>#{payment.transaction_id}</TxId>
          </PmtId>
          <IntrBkSttlmAmt Ccy="BRL">#{payment.amount}</IntrBkSttlmAmt>
          <!-- ... additional fields ... -->
        </CdtTrfTxInf>
      </FIToFICstmrCdtTrf>
    </Document>
    """
  end
end

JWS Signing

All SPI messages must be signed using JWS (JSON Web Signature) with the institution's private key:

elixir
defmodule Shared.Bacen.JwsSigner do
  def sign(payload, private_key) do
    header = %{
      "alg" => "PS256",
      "typ" => "JWT",
      "kid" => key_id(private_key)
    }

    JOSE.JWS.sign(private_key, payload, header)
  end
end

Compliance Requirements

Operational Hours

PIX operates 24/7/365. The platform must maintain availability at all times, including:

  • Real-time payment processing (SPI)
  • Key management (DICT)
  • Settlement cycles (multiple daily windows)

Response Time Requirements

BACEN mandates specific response times:

OperationMaximum Response Time
Key lookup500ms
Payment initiation1,000ms
Payment confirmation500ms
Claim processing10 seconds

Data Retention

  • Transaction data: minimum 5 years
  • Audit logs: minimum 10 years
  • Key history: minimum 5 years after key removal

MED 2.0 (Mecanismo Especial de Devolucao)

MED 2.0 is BACEN's fraud prevention mechanism. The platform supports:

  1. Infraction reports - Flag suspicious transactions
  2. Special returns - Automated reversal for confirmed fraud
  3. Account blocking - Temporary hold on suspected fraud accounts
  4. Notification relay - Forward MED notifications to affected participants
elixir
defmodule DictService.Med do
  def create_infraction(params) do
    Shared.Bacen.Client.post("/api/v2/infraction", %{
      "EndToEndId" => params.end_to_end_id,
      "InfractionType" => params.type,  # "FRAUD" | "REQUEST_REFUND"
      "ReportDetails" => params.details
    })
  end
end

Error Handling

BACEN APIs return structured error responses. The platform maps these to internal error types:

BACEN ErrorInternal Handling
KEY_NOT_FOUNDReturn 404, log lookup miss
KEY_ALREADY_CLAIMEDQueue for claim resolution
ENTRY_LOCKEDRetry with exponential backoff
INSUFFICIENT_BALANCEReject payment, notify user
RATE_LIMITEDQueue and retry after cooldown
CERTIFICATE_ERRORAlert ops team, circuit break

The BACEN client implements circuit breaker pattern to prevent cascading failures:

elixir
defmodule Shared.Bacen.CircuitBreaker do
  use GenServer

  @failure_threshold 5
  @reset_timeout :timer.seconds(30)

  # States: :closed (normal), :open (failing), :half_open (testing)
end

FluxiQ PIX - Plataforma Brasileira de Pagamento Instantaneo