Reporter lets you generate XML-based reports that follow the official CADOC structure, exactly as required by the Brazilian Central Bank (BACEN).
This guide walks you through the structure and logic used to generate CADOC 4010 and CADOC 4016 reports in XML.
These reports follow the COSIF standard and must match the XML structure defined by BACEN. You can adapt the logic to your own data model, but the XML format must be respected.
What are CADOC 4010 and 4016?
The 4010 and 4016 are Analytical Balance Sheet reports used to submit financial reports to the BACEN (Central Bank of Brazil).
What BACEN expects to receive
- Closing balances by COSIF code
- Report base date (reference month)
- Institution’s CNPJ (first 8 digits)
- Submission type (
I = Inclusion, S = Replacement)
Difference between CADOC 4010 and 4016
| Document | Periodicity | Base Date | Expected Accounts |
|---|
| 4010 | Monthly | YYYY-MM | All accounts |
| 4016 | Semiannual (Jun/Dec) | YYYY-MM | Without groups 7 and 8 (Revenues/Expenses) |
The 4016 represents the entity’s accounting position after the income statement closing. At this point, Revenue accounts (group 7) and Expense accounts (group 8) have already been closed and their balances transferred to Equity.
Submission requirements
| Document | Deadline | STA Code |
|---|
| 4010 | Day 18 of the following month (or the next business day) | ACOS010 |
| 4016 | Last business day of the following month | ACOS016 |
The STA code identifies the document type in BACEN’s transmission system. Use ACOS010 when submitting CADOC 4010 and ACOS016 when submitting CADOC 4016.
Understanding the data structure
Operation routes
Operation routes work as accounting classifiers. Each route has:
- Unique identifier (
id): Used internally to relate operations
- COSIF code (
code): The 10-digit accounting code that will be reported to BACEN
Think of routes as “accounting labels” attached to each operation, indicating which chart of accounts item that movement should be classified under.
Operations
Operations represent financial movements in the ledger. Each operation contains:
- Associated account (
account_id): Which account was moved
- Route (
route): ID of the applied accounting route/classification
- Balance after operation (
available_balance_after): The account balance immediately after this operation
- Date and time (
created_at): When the operation occurred
Relationship between entities
The balance entity represents the current account balance, not the history. For regulatory reports that need balances from a specific period, use the operation entity with date filters.
CADOC structure
The CADOC report must be an XML file and must follow the structure defined by BACEN:
<?xml version="1.0" encoding="UTF-8"?>
<documento codigoDocumento="4010" cnpj="99999999" dataBase="2025-11" tipoRemessa="I">
<contas>
<conta codigoConta="1000000009" saldo="99.99" />
<conta codigoConta="1100000002" saldo="99.99" />
</contas>
</documento>
Mandatory fields
<?xml version="1.0" encoding="UTF-8"?>
Always starts the file. It defines the XML version and encoding so the system knows how to read the content.
<documento> tag
Wraps the entire CADOC structure and includes:
| Field | Description | Format |
|---|
codigoDocumento | Fixed identifier | "4010" or "4016" |
cnpj | First 8 digits of CNPJ | Numeric, 8 positions |
dataBase | Reference month | YYYY-MM |
tipoRemessa | Submission type | "I" or "S" |
If your first submission was rejected due to errors, you still need to use "I" on your next attempt. Only use "S" for replacing previously approved data.
<contas> tag
Groups all account entries for the reporting period.
<conta> tag
codigoConta: Account code, following COSIF format (10 numeric digits).
saldo: Account balance in decimal format (two decimal places).
Template construction logic
General structure
The template follows a two-level aggregation logic:
- First level: Iterate through all available operation routes
- Second level: For each route, aggregate the balances of linked operations
Iterating over routes
The template must iterate through all registered operation routes. For each route:
- Check if it has a COSIF code: Only routes with a filled code generate lines
- Filter operations: Select operations that belong to that route
- Calculate the balance: Aggregate the balances of filtered operations
Variable naming caution
When building the template, avoid using the same name for the iteration variable and the filter field:
| Approach | Example | Result |
|---|
| Incorrect | for route in ... if route == route.id | Name conflict |
| Correct | for op_route in ... if route == op_route.id | Works correctly |
CADOC 4010 template
Here’s the complete template for generating CADOC 4010 in Reporter:
<?xml version="1.0" encoding="UTF-8"?>
<documento codigoDocumento="4010" cnpj="{{ midaz_onboarding.organization.0.legal_document|slice:':8' }}" dataBase="{% date_time 'YYYY-MM' %}" tipoRemessa="I">
<contas>
{%- for op_route in midaz_transaction.operation_route %}
{%- if op_route.code %}
<conta codigoConta="{{ op_route.code }}" saldo="{% sum_by midaz_transaction.operation by "available_balance_after" if route == op_route.id %}" />
{%- endif %}
{%- endfor %}
</contas>
</documento>
Line-by-line explanation
Line 1 - XML declaration
Standard XML header with UTF-8 encoding.
Line 2 - Root element <documento>
codigoDocumento="4010": Fixed identifier for the Analytical Balance Sheet
cnpj: Extracts the first 8 digits from the organization’s legal document
dataBase: Generates the date in YYYY-MM format (reference month)
tipoRemessa="I": Indicates data inclusion
Line 4 - Start of for loop
op_route: Variable that receives each route during iteration
midaz_transaction.operation_route: Collection of all operation routes
Line 5 - if condition
Checks if the route has a filled COSIF code.
Line 6 - <conta> element
codigoConta: Displays the current route’s COSIF code
saldo: Uses the sum_by tag to aggregate balances, filtering operations whose route matches the current route’s identifier (op_route.id)
Lines 7-8 - Block closing
Close the condition and loop respectively.
| Element | Type | Function |
|---|
{{ variable }} | Expression | Displays a variable’s value |
{% tag %} | Tag | Executes logic (loop, condition, aggregation) |
|slice:':8' | Filter | Cuts the first 8 characters |
for ... in | Tag | Iterates through a collection |
if | Tag | Condition for execution |
sum_by ... by ... if | Tag | Sums values with conditional filter |
date_time | Tag | Generates formatted date |
CADOC 4016 template
The CADOC 4016 is the Analytical Balance Sheet, similar to 4010, but with semiannual periodicity and an important restriction: it must not contain accounts from groups 7 (Revenues) and 8 (Expenses).
Why exclude groups 7 and 8?
The 4016 document represents the entity’s accounting position after the income statement closing. At this point, Revenue accounts (group 7) and Expense accounts (group 8) have already been closed and their balances transferred to Equity.
Template example
<?xml version="1.0" encoding="UTF-8"?>
<documento codigoDocumento="4016" cnpj="{{ midaz_onboarding.organization.0.legal_document|slice:':8' }}" dataBase="{% date_time 'YYYY-MM' %}" tipoRemessa="I">
<contas>
{%- for op_route in midaz_transaction.operation_route %}
{%- if op_route.code and op_route.code|slice:":1" != "7" and op_route.code|slice:":1" != "8" %}
<conta codigoConta="{{ op_route.code }}" saldo="{% sum_by midaz_transaction.operation by "available_balance_after" if route == op_route.id %}" />
{%- endif %}
{%- endfor %}
</contas>
</documento>
Difference from 4010
The only difference in the template is the additional condition in the if:
| Template | Condition |
|---|
| 4010 | if op_route.code |
| 4016 | if op_route.code and op_route.code|slice:":1" != "7" and op_route.code|slice:":1" != "8" |
How the exclusion works
The slice:":1" filter extracts the first character of the COSIF code:
| COSIF Code | First Digit | Group | Included in 4016? |
|---|
| 1000000009 | 1 | Assets | Yes |
| 2100000003 | 2 | Liabilities | Yes |
| 7100000001 | 7 | Revenues | No |
| 8200000005 | 8 | Expenses | No |
Generating the report with date filter
Request example for 4010
To generate the CADOC 4010 for a specific month, send a POST /v1/reports request with the X-Organization-Id header and the following body:
{
"templateId": "CADOC_4010_TEMPLATE_ID",
"filters": {
"midaz_transaction": {
"operation": {
"created_at": {
"between": ["2025-11-01T00:00:00Z", "2025-11-30T23:59:59Z"]
}
}
}
}
}
Field explanation
| Field | Description |
|---|
templateId | Identifier of the CADOC 4010 template registered in Reporter |
filters.midaz_transaction.operation | Indicates the filter will be applied to the operations collection |
created_at.between | Filters operations created within the specified interval |
between[0] | First day of the month at 00:00:00 UTC |
between[1] | Last day of the month at 23:59:59 UTC |
Date format: ISO 8601 with UTC timezone (Z).
Adjust the last day according to the month (28, 29, 30, or 31 days).
Request example for 4016
To generate the CADOC 4016 for the first semester:
{
"templateId": "CADOC_4016_TEMPLATE_ID",
"filters": {
"midaz_transaction": {
"operation": {
"created_at": {
"between": ["2025-01-01T00:00:00Z", "2025-06-30T23:59:59Z"]
}
}
}
}
}
For the second semester, adjust the dates to July through December.
Template evolution and balance extraction
We are working on evolving our main template to support balance aggregation in compliance with BACEN CADOC 4010/4016, which requires the final balance from the last business day of the month.
While this feature is being developed, we provide an alternative version for extracting these balances using the following template.
This auxiliary template is designed to correctly calculate balances, ensuring compliance of your reports through the following steps:
- Group operations by account
- Sort entries by date and time
- Select the last record from each account to obtain the final balance
- Sum final balances by COSIF code
With this option, you can extract the information required for CADOC 4010/4016 and transfer it to the layout format required by BACEN. This template can help if you already have a provider that builds CADOC files.
account_id;account_alias;codigo_cosif;created_at;saldo_disponivel
{%- for op_route in midaz_transaction.operation_route %}
{%- if op_route.code %}
{%- for op in filter(midaz_transaction.operation, "route", op_route.id) %}
{{ op.account_id }};{{ op.account_alias }};{{ op_route.code }};{{ op.created_at }};{{ op.available_balance_after }}
{%- endfor %}
{%- endif %}
{%- endfor %}
The template exports all operations in CSV format, containing:
- Account identifier
- Account alias
- COSIF code
- Operation date and time
- Available balance
You can import this CSV into a spreadsheet or your existing reconciliation system to process the final balances.
Comparison with CADOC 4111
CADOC 4010 and CADOC 4111 share the same template structure, with differences only in the parameters:
| Aspect | CADOC 4111 | CADOC 4010 |
|---|
| Document | Daily Balances | Monthly Balance Sheet |
| Periodicity | Daily | Monthly |
codigoDocumento | "4111" | "4010" |
dataBase | YYYY-MM | YYYY-MM |
| Date filter | 1 day | 1 month |
| Expected balance | Last of the day | Last of the month |
Identical template: The logic for iterating over routes and aggregating balances is the same.
Best practices for building templates
Variable naming
Use descriptive and unique names for iteration variables, avoiding conflicts with entity field names.
Field validation
Always check if optional fields have values before using them. Empty fields can generate unwanted lines in the report.
BACEN requires dates in YYYY-MM format for 4010. Make sure to configure the format correctly in the template.
CNPJ handling
The CNPJ must be presented with only the first 8 digits, without formatting (dots, slashes, or dashes).
COSIF account code
The COSIF chart of accounts is structured with 6 hierarchical levels and a check digit. The codigoConta field must have 10 numeric digits, without dots or dashes.
Component summary
| Component | Data Source | Usage in Template |
|---|
| CNPJ | Organization (Onboarding) | Document header |
| COSIF Code | Operation Route (Transaction) | Account identifier |
| Balance | Operation (Transaction) | Value to be aggregated |
| Base Date | Current date function | Document header |
Always validate the rendered XML against BACEN’s schema before submitting. The structure alone isn’t enough — the data must reflect your institution’s actual ledger.