Collect self-certifications
Collect a tax-residency self-certification from each account owner and check that it's valid, for CARF and DAC8 reporting.
Part of CARF & DAC8 Reporting.
The self-certification can come from either of two routes: the React SDK, where the account owner fills in the SELF-CERT questionnaire in your app, or the API, where you send a self-certification you hold from your backend. Digital platforms reporting seller income use a different questionnaire, the DPS; see Collect seller data.
Before you begin
- A bearer token. See Authenticate.
- The account owner and its account. See Create account owners and accounts.
1. Set up the account owner
The account owner's account_owner_type is INDIVIDUAL or ENTITY. Set its self_certification_classification, which is used to determine CARF and DAC8 reporting obligations, and its tax_residencies, one entry per country the account owner is resident in for tax:
curl -X PATCH $BASE_URL/v1/account-owners/usr_4d8a2e61 \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"self_certification_classification": "INDIVIDUAL",
"tax_residencies": [
{ "country": "IT", "tin": "RNLSFO91H42F205X", "tin_type": "OTHER" }
]
}'A 200 returns the updated account owner in data. self_certification_classification takes the same values as classification on the self-certification in step 2.
If you collected the account owner's self-certification outside Taxbit and won't send it, set valid_self_certification_on_file to say whether a valid one is on file. Once a self-certification is on file in Taxbit, the field reflects that document's validation status instead of the value you set.
The other account owner fields are on Create account owners and accounts and Collect seller data.
2. Collect the self-certification
In your app, with the SDK
Render the questionnaire with questionnaire="SELF-CERT". Set data.accountHolder.isIndividual to true for an individual or false for an entity; an entity also needs selfCertificationAccountType (FINANCIAL_INSTITUTION, ACTIVE_NON_FINANCIAL_ENTITY, or PASSIVE_NON_FINANCIAL_ENTITY). The Questionnaire integration guide covers installing the SDK and rendering the questionnaire, entity flows included.
From your backend, with the API
Send the self-certification you hold. Here the account owner is an individual resident in Italy:
curl -X POST $BASE_URL/v1/account-owners/usr_4d8a2e61/tax-documentation-data/self-certification \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Sofia Rinaldi",
"classification": "INDIVIDUAL",
"permanent_address": {
"first_line": "Via dei Glicini 18",
"city": "Milan",
"postal_code": "20144",
"country": "IT"
},
"tax_residences": [
{ "country": "IT", "tin": "RNLSFO91H42F205X" }
],
"date_of_birth": "1991-06-02",
"city_of_birth": "Milan",
"country_of_birth": "IT",
"country_of_citizenship": "IT",
"has_signed_and_certified": true,
"signature_date": "2026-10-07T00:00:00.000Z"
}'A 201 returns the stored submission in data, with TINs masked. On the self-certification the residence list is tax_residences, without a tin_type; on the account owner it is tax_residencies.
The endpoint stores what you send and doesn't reject incomplete or inconsistent data. Taxbit validates the submission and reports problems through the status in step 3, so send the complete form, then read the status.
A few rules for the fields:
- For a country where the account owner has no TIN, send the entry with
tin_not_requiredset totrueand atin_not_required_reasonofNOT_ISSUED,NOT_REQUIRED, orOTHER(withtin_not_required_reason_otherforOTHER). Without the reason, the status reports an issue. signatureis the name signed on the form, and it requireshas_signed_and_certifiedto betrue. For an individual you can leave it out: Taxbit derives the signature fromname.- Country fields take a two-letter ISO 3166-1 alpha-2 code, such as
IT.
Entities. For an entity, classification is one of these values. NFE means non-financial entity.
| Group | classification | Entity |
|---|---|---|
| Financial institution | FINANCIAL_INSTITUTION_DEPOSITORY_INSTITUTION | Depository institution |
| Financial institution | FINANCIAL_INSTITUTION_CUSTODIAL_INSTITUTION | Custodial institution |
| Financial institution | FINANCIAL_INSTITUTION_INSURANCE_COMPANY | Insurance company |
| Financial institution | FINANCIAL_INSTITUTION_NON_REPORTING | Non-reporting financial institution |
| Financial institution | FINANCIAL_INSTITUTION_INVESTMENT_ENTITY_MANAGED | Managed investment entity |
| Financial institution | FINANCIAL_INSTITUTION_INVESTMENT_ENTITY_OTHER | Other investment entity |
| Active NFE | ACTIVE_NFE_GOVERNMENT_ENTITY | Government entity |
| Active NFE | ACTIVE_NFE_CENTRAL_BANK | Central bank |
| Active NFE | ACTIVE_NFE_INTERNATIONAL_ORGANIZATION | International organization |
| Active NFE | ACTIVE_NFE_PUBLIC_CORPORATION | Public corporation |
| Active NFE | ACTIVE_NFE_OTHER | Any other active NFE |
| Passive NFE | PASSIVE_NFE | Passive NFE; needs at least one controlling person (below) |
A PASSIVE_NFE self-certification must include at least one entry in controlling_persons, each with the person's name, role, and their own address, birth, and tax_residences details; without one, the status reports that "at least one controlling person must be provided for classification PASSIVE_NFE". The person who signs for the entity goes in signature, and their capacity in signature_capacity (OFFICER, EXECUTOR, or OTHER_CAPACITY, with signature_capacity_other for the last). The SDK's SELF-CERT questionnaire walks entities through all of this in your app.
3. Check the status
Read the account owner's tax documentation status:
curl $BASE_URL/v1/account-owners/usr_4d8a2e61/tax-documentation-status \
-H "Authorization: Bearer $TOKEN"For the submission above:
{
"self_certification": {
"data_collection_status": "COMPLETE",
"tax_documentation_status": "VALID",
"needs_resubmission": false,
"issues": []
}
}self_certification reports on the latest self-certification:
data_collection_statusisCOMPLETEorINCOMPLETE: whether the required information has been collected.tax_documentation_statusisVALIDwhen the most recently submitted self-certification has passed validation checks, orINVALIDwhen it has issues or missing information to correct before it can be used for reporting.needs_resubmissionsays whether to prompt the account owner to submit a new self-certification.issueslists what needs fixing. Each issue has anissue_type, astatus(OPEN,IN_REVIEW, orRESOLVED), anddetails, each naming afieldand describing the problem.
A submission that left out tin_not_required_reason, for example, reads:
{
"self_certification": {
"data_collection_status": "INCOMPLETE",
"tax_documentation_status": "INVALID",
"needs_resubmission": true,
"issues": [
{
"issue_type": "INCOMPLETE_DATA",
"status": "OPEN",
"created_at": "2026-10-07T20:11:52.741Z",
"details": [
{
"field": "tax_residences.0.tin_not_required_reason",
"description": "TIN-not-required reason must be provided when TIN is not required"
}
]
},
{
"issue_type": "INCONSISTENT_DATA",
"status": "OPEN",
"created_at": "2026-10-07T20:11:52.741Z",
"details": [
{
"field": "tax_residences.0.tin_not_required_reason",
"description": "must be provided"
}
]
}
]
}
}The issue types that apply to self-certifications are INCOMPLETE_DATA, INCONSISTENT_DATA, INCOMPLETE_ADDRESS, US_INDICIA, CHANGE_IN_CIRCUMSTANCES, CBI_RBI_CONFIRMATION, and INCOMPLETE_GIIN; the SDK component reference describes each.
Fixing issues. Correct the fields the issues name and submit the complete form again. The status reports on your latest submission: once it passes, the issues are gone and needs_resubmission is false.
On the account owner, valid_self_certification_on_file is the one-field summary. It is absent until a self-certification exists, then reflects the latest one's validation status, and is true once the status is VALID.
The stored data. GET /v1/account-owners/{id}/tax-documentation-data returns the stored self-certification under selfCertification, with document_type SELF_CERTIFICATION, is_individual, and, for an entity, self_certification_account_type. TINs are masked unless you pass unmask=true.
If the request fails
These apply to the self-certification POST.
- 400. The request is malformed. Incomplete or inconsistent data doesn't return a 400; it shows up as issues in the status.
- 401. The bearer token is missing, invalid, or expired. Request a new one and retry.
- 404. No account owner has that
id. Create it first, as Create account owners and accounts shows. - 429. Too many requests. Slow down and retry.
The Errors and rate limits page has every error shape, the rate limits, and which calls are safe to retry.
Parameters
Only the fields used above. "Required for this workflow" marks what a signed, complete self-certification needs; the status in step 3 lists any field a submission is still missing. The full schemas, with what the API itself requires, are in the Self-certification reference and the Account Owners reference.
POST /v1/account-owners/{id}/tax-documentation-data/self-certification
| Field | Type | Required for this workflow | Description |
|---|---|---|---|
name | string | Optional | The account owner's legal name. |
classification | string | Optional | INDIVIDUAL, or one of the entity values listed in step 2. |
permanent_address | object | Optional | first_line, second_line, city, state_or_province, postal_code, and country. |
tax_residences | array | Required | One entry per country of tax residence: country, and tin or tin_not_required with tin_not_required_reason. |
date_of_birth | string | Optional | ISO 8601 date of birth. |
city_of_birth | string | Optional | City of birth. |
country_of_birth | string | Optional | Country of birth, as a two-letter code. |
country_of_citizenship | string | Optional | Country of citizenship, as a two-letter code. |
controlling_persons | array | Required for PASSIVE_NFE | For an entity: name, role, ownership_percentage, date_of_birth, city_of_birth, country_of_birth, country_of_citizenship, address, and tax_residences. |
has_signed_and_certified | boolean | Required | Whether the account owner signed and certified the form. |
signature_date | string | Optional | ISO 8601 timestamp of the signature. |
signature | string | Optional | The name signed. Requires has_signed_and_certified. Derived from name for an individual when left out. |
signature_capacity | string | Optional | For an entity: OFFICER, EXECUTOR, or OTHER_CAPACITY. |
PATCH /v1/account-owners/{id}
| Field | Type | Required for this workflow | Description |
|---|---|---|---|
self_certification_classification | string | Optional | Used to determine CARF and DAC8 reporting obligations. Same values as classification. |
tax_residencies | array | Optional | country, tin, and tin_type. Set to null to clear. |
valid_self_certification_on_file | boolean | Optional | Whether a valid self-certification is on file. Replaced by the latest self-certification's validation status once one is on file in Taxbit. |
Where to go next
- Send crypto-asset activity: tag stablecoins, send each account owner's transactions, and find what Taxbit produces.
- Webhook events: the
ACCOUNT_OWNER_TAX_DOCUMENTATION_STATUSevent carries theself_certificationstatus whenever it changes, so you don't have to poll. - The Questionnaire integration guide for collecting self-certifications in your app.
- Collect seller data for the DPS route digital platforms use.
Updated 4 days ago
