Integration Guide
Collect W-9, W-8 (BEN, BEN-E, IMY), MRDP (DAC7), CRS, and CARF tax documentation inside your React application with a drop-in, fully customizable component. Submitted data flows into the Taxbit platform, undergoes TIN matching where applicable, and feeds end-of-year information returns. Completed forms are available from the Taxbit Dashboard and the Tax Documentation API.
The SDK supports three questionnaire types:
| Questionnaire | Use it for |
|---|---|
Forms W-8 / W-9 (W-FORM) | Collect and validate Forms W-8 and W-9 ahead of payment and reporting for 1099-B, DIV, INT, and more. |
Digital Platform Seller (DPS) | Satisfy MRDP obligations under DAC7 (EU), and equivalents in the UK, New Zealand, and Canada. |
Self-Certification (SELF-CERT) | Collect CRS, CARF, and DAC8 self-certifications based on OECD guidance for global reporting. |
You'll pick the one that fits your users under Configure for your use case.
Set up locally
Render the SDK in demo mode to verify it works in your app. Demo mode requires no token and makes no requests to Taxbit.
Install the SDK
npm install @taxbit/react-sdkCompatible with React 16–19 and TypeScript 5.
Render in demo mode
import '@taxbit/react-sdk/style/inline.css';
import { TaxbitQuestionnaire } from '@taxbit/react-sdk';
export default function App() {
return <TaxbitQuestionnaire questionnaire="W-FORM" demoMode={true} />;
}Set questionnaire to "DPS" or "SELF-CERT" to render the other forms. onProgress and onSubmit fire in demo mode, so you can inspect the data the SDK collects.
What you should see
The component renders the questionnaire as a multi-step form: a progress indicator, one group of questions per step, inline validation, and Back / Next / Submit controls. onProgress and onSubmit fire as the user moves through it, so you can confirm data is flowing.
The styling is Taxbit's default, not a fixed design. The form is plain semantic HTML with taxbit- class names — point it at your own stylesheet to match your product. See Style it to your brand.
If the form doesn't render, verify the stylesheet import resolved and your React version is supported. Next, connect your backend to submit real data.
Connect your backend
The SDK authenticates with a bearer token scoped to a single Account Owner — the user you're collecting documentation from. Mint the token on your server; token requests from the browser are rejected.
Gather your credentials Required
RequiredLog in to the Taxbit Dashboard and navigate to Settings > Developer Settings to find your environment-specific credentials.
| Credential | Purpose |
|---|---|
client_id | Public identifier for your integration |
client_secret | Server-side only. Never expose to the browser |
tenant_id | Identifies your organization in Taxbit |
Keep your client_secret secure. Store it in a secrets manager or environment variable. Anyone with the secret can impersonate your tenant.
Non-US tenants and proxied networks. EU-provisioned tenants pass region="EU" (the default is "US"); the server-side API host is region-specific too. Where direct access to Taxbit is restricted, route requests through your own gateway with proxyDomain, plus proxyHeaders for custom auth (your proxy must strip them before forwarding).
Create an Account Owner Required
RequiredCreate an Account Owner for each user you collect from. Authorize this tenant-wide call with a tenant-scoped token from POST /v1/oauth/token. Only id and account_owner_type are required.
| Method | Endpoint |
|---|---|
POST | /v1/account-owners |
const aoResponse = await fetch(
"https://api.multi1.enterprise.taxbit.com/v1/account-owners",
{
method: "POST",
headers: {
"Authorization": `Bearer ${tenantToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
id: "d290f1ee-6c54-4b01-90e6-d701748f0851", // V4 UUID recommended
account_owner_type: "INDIVIDUAL", // INDIVIDUAL or ENTITY
}),
}
);The id you provide becomes the account_owner_id in all subsequent API calls.
Provisioning strategy. For new users, create the Account Owner during onboarding, when the user creates their account in your system — only the ID is needed. For existing users, both the API and file ingestion are available for backfilling Account Owner data.
Account creation is optional for the SDK. The SDK doesn't require an Account. You may need Accounts later for transaction reporting — see the Accounts API.
Mint an Account Owner token
Exchange your credentials for an Account Owner–scoped token and pass access_token to your client.
const { access_token } = await fetch(
"https://api.multi1.enterprise.taxbit.com/v1/oauth/account-owner-token",
{
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: process.env.TAXBIT_CLIENT_ID,
client_secret: process.env.TAXBIT_CLIENT_SECRET,
tenant_id: process.env.TAXBIT_TENANT_ID,
account_owner_id: accountOwnerId,
}),
}
).then((r) => r.json());Token expiration
If a token expires mid-session, API calls return 401 Unauthorized and surface through your onError callback. See Handling token expiration for how to detect and recover from it.
Render with real data
Replace the demoMode prop with the bearerToken prop. The same component now submits to Taxbit:
<TaxbitQuestionnaire
questionnaire="W-FORM"
bearerToken={accountOwnerToken}
onSuccess={handleSuccess}
/>Submissions appear in your Taxbit Dashboard and are retrievable from the tax-documentation-status endpoint. onSuccess fires when Taxbit receives the submission, not when validation completes — see Handle validation and status.
Update your Content Security Policy If applicable
If applicableIf your app uses a Content Security Policy, add the following directive to allow the SDK to reach the Taxbit API:
connect-src https://*.taxbit.com;
Without this, the SDK renders but API calls will fail — check your browser's network and console errors and your onError handler before testing in production.
Configure for your use case
With real data flowing, shape the flow to fit your users: which questionnaire you need, and how to avoid asking for data you already have.
Pick your questionnaire
Each pattern below leads with the situation that determines the configuration. If more than one applies to your user base — for example, U.S. and non-U.S. payees — render the component multiple times with different questionnaire values per Account Owner.
Paying U.S. persons subject to 1099 reporting
W-FORM. Collect Form W-9 from U.S. individuals or entities who'll receive a 1099 at year-end. Use this whenever you need a certified U.S. TIN before making a reportable payment.
| Prop | Setting |
|---|---|
questionnaire | "W-FORM" |
realTimeTinValidation | true if your tenant is configured for RTT — surfaces TIN mismatches at submission rather than at year-end. |
treatyClaims | Omit — treaty claims don't apply to W-9 filers. |
Watch for: a returning tinStatus of MISMATCH or INVALID_DATA after submission — these need a B-notice flow. See TIN validation.
Paying foreign persons U.S.-source income (FDAP)
W-FORM. Collect Form W-8BEN or W-8BEN-E. If the payee may claim reduced withholding under a tax treaty, enable treatyClaims to surface the additional questions about treaty country, article, and claimed rate.
| Prop | Setting |
|---|---|
questionnaire | "W-FORM" |
treatyClaims | true if any of your foreign payees may claim treaty benefits. Safe to leave on universally — payees not claiming a treaty move through the questions without committing to anything. |
realTimeTinValidation | No effect — RTT applies only to W-9 submissions. |
Watch for: treatyClaimStatus: 'INVALID' and the TREATY_COUNTRY_MISMATCH issue type both mean the treaty claim won't apply as submitted. The needsResubmission flag on wFormQuestionnaire tracks whether the user needs to return. See validation and status.
Collecting CRS, CARF, or DAC8 self-certification
SELF-CERT. Collect tax residences, citizenship, and TINs from individuals or entities on platforms subject to CRS, CARF, or DAC8 reporting. The same questionnaire serves both, with the payload shape differing by entity type.
| Prop | Setting |
|---|---|
questionnaire | "SELF-CERT" |
data.accountHolder.isIndividual | true for individuals; false for entities. Entities also need selfCertificationAccountType set to "FINANCIAL_INSTITUTION", "ACTIVE_NON_FINANCIAL_ENTITY", or "PASSIVE_NON_FINANCIAL_ENTITY". |
language | Defaults to en-GB. Set explicitly for non-English markets. |
Watch for: tax residences in high-risk jurisdictions trigger an additional accountHolderTaxResidenciesConfirmation step. Controlling-person data on entity flows cannot be pre-populated — those questions are always shown, even with adaptive mode enabled.
Running a digital platform with MRDP reporting obligations
DPS. Collect seller information for DAC7 (EU) and equivalents in the UK, New Zealand, and Canada. The SDK collects tax identification, address, business registration, and (where applicable) VAT.
| Prop | Setting |
|---|---|
questionnaire | "DPS" |
language | Defaults to en-GB. Set per-seller based on locale; full EU language coverage is supported. |
Watch for: VAT validation via VIES is asynchronous — vatStatus may return 'PENDING' on first submission and resolve later. See VAT validation. DPS submissions don't have a downloadable PDF — canGetDocumentUrl on the hook will be false.
Adaptive mode: skip pre-filled questions
Use Adaptive mode when you already collect user data upstream — during onboarding, KYC, KYB, or account maintenance — and want the SDK to ask only for what's still missing, invalid, or required for the tax form.
Think of it as a bridge between your application flow and Taxbit's tax documentation flow: you keep your existing upstream experience, pass known data in through the data prop, and the SDK shortens the questionnaire by hiding fields already satisfied with valid values. It evaluates each field independently:
- Valid values hide the question.
- Invalid or incomplete values are shown so the user can correct them.
- Required fields still appear when missing.
Note: Adaptive mode reduces duplicate data entry, but it doesn't remove required certification, confirmation, or review steps. Some sections also can't be pre-populated — controlling-person data in certain Self-Cert entity flows, for example.
What it does and doesn't do
It does:
- Hide questions for fields you already provided with valid values.
- Shorten the flow so users focus only on what still needs attention.
- Support different review experiences depending on whether skipped values should be locked or editable.
It doesn't:
- Bypass required tax logic or certification requirements.
- Silently accept invalid upstream data — invalid or incomplete values are still shown for correction.
- Guarantee every section can be pre-populated — some are always collected directly in the SDK.
When to use it
Adaptive mode is a strong fit when you already collect some or most of the same information upstream and want tax documentation to feel like a continuation of your existing flow rather than a separate questionnaire. Common cases:
- End-of-onboarding tax documentation after KYC or KYB.
- Existing-user resubmission or change-in-circumstance flows.
- Account settings or maintenance flows where you re-open the SDK with as much prior data preserved as possible.
Choose a mode
<TaxbitQuestionnaire
questionnaire="W-FORM"
adaptiveMode="skipLock"
bearerToken={token}
data={{
accountHolder: {
isUsPerson: true, usAccountType: "INDIVIDUAL",
name: "Jane Doe", tin: "776568989",
address: { firstLine: "123 Main St", city: "Seattle",
stateOrProvince: "WA", postalCode: "98101", country: "US" },
},
}}
/>| Mode | In the flow | On the review screen | Best for |
|---|---|---|---|
full (default) | Nothing skipped; supplied data is pre-filled | All fields editable | Prefill only, without hiding questions |
skipLock | Questions with valid data are hidden | Skipped fields are locked | Onboarding flows where upstream data should change only when strictly necessary |
skipEdit | Questions with valid data are hidden | All fields remain editable | Resubmission or maintenance flows where users may need to adjust prior values |
How skip logic works
Adaptive reads three signals per field: whether it's present in data, whether the value is valid, and whether it's required for the form type.
| What you pass | In the flow | Review (skipLock) |
|---|---|---|
| Valid value | Hidden | Locked |
| Invalid or incomplete | Shown to fix | Editable |
"" on an optional field | Hidden | Locked |
"" on a required field | Shown — never skipped on "" | Editable |
| Omitted (optional) | Hidden | Editable — generally nothing to lock |
| Omitted (required) | Shown | Editable |
Invalid or incomplete data — a partial address, an invalid postal code, a wrong date format, a malformed TIN — is always shown so the user can correct it.
Empty string vs. omitted
Pass "" only for a field you already showed the user in your own UI and they chose to leave blank; the SDK reads it as "asked and intentionally skipped" and won't ask again. This applies to optional fields only — a required field with "" is treated as missing and still shown.
Don't pass "" to shorten the flow for a field the user never saw. Some fields — a W-8 mailing address, a W-8 U.S. TIN — are optional in the SDK but should still be offered; an empty string there produces a form that's valid in the SDK but invalid under tax rules.
FTIN
Non-U.S. TINs with syntax warnings are still skipped (and locked under skipLock). Setting ftinNotLegallyRequired: true overrides the FTIN entirely — the SDK treats the field as not required.
Example: what the user still sees
Suppose you collected a user's name, address, and TIN during onboarding and passed those into the SDK. If the values are valid:
- The user doesn't re-enter them in the SDK.
- The SDK asks only for fields that are still missing, invalid, or required for the tax form.
- The user still completes any required certifications, confirmations, or always-collected sections.
The more complete and valid your upstream data, the shorter the SDK flow — which is why Adaptive mode works best paired with a clear upstream data strategy.
Prefill data by form type
Adaptive reads the data prop. The fields it accepts differ by form type — expand a shape to see what you can pre-fill. Which props to set for each questionnaire is covered under Pick your questionnaire.
W-9 — Individual
{
"accountHolder": {
"isUsPerson": true,
"usAccountType": "INDIVIDUAL",
"name": "Jane Doe",
"tin": "776568989",
"address": {
"firstLine": "123 Main St",
"secondLine": "",
"city": "Seattle",
"stateOrProvince": "WA",
"postalCode": "98101",
"country": "US"
}
}
}W-9 — Entity (C Corporation)
{
"accountHolder": {
"isUsPerson": true,
"usAccountType": "C_CORPORATION",
"name": "Martinez Corporation",
"dbaName": "Martinez Solutions",
"tin": "776568989",
"address": {
"firstLine": "123 Main St",
"secondLine": "",
"city": "Seattle",
"stateOrProvince": "WA",
"postalCode": "98101",
"country": "US"
}
}
}W-8BEN
{
"accountHolder": {
"isUsPerson": false,
"accountOwnerType": "INDIVIDUAL",
"name": "Maria Fernandez",
"ftin": "A1234567",
"address": {
"firstLine": "45 Calle Real",
"secondLine": "Apt 3",
"city": "Madrid",
"stateOrProvince": "MD",
"postalCode": "28001",
"country": "ES"
},
"mailingAddress": {
"firstLine": "PO Box 123",
"secondLine": "",
"city": "Madrid",
"stateOrProvince": "MD",
"postalCode": "28002",
"country": "ES"
},
"mailingAddressIsDifferent": true,
"countryOfCitizenship": "ES",
"dateOfBirth": "01/01/1990",
"ftinNotLegallyRequired": false
}
}W-8BEN-E
{
"accountHolder": {
"isUsPerson": false,
"accountOwnerType": "ENTITY",
"countryOfCitizenship": "US",
"foreignAccountType": "CORPORATION",
"name": "Fernandez Consulting Group",
"ftin": "B9876543",
"tin": "",
"address": {
"firstLine": "100 Business Rd",
"secondLine": "Suite 500",
"city": "Barcelona",
"stateOrProvince": "BC",
"postalCode": "08001",
"country": "ES"
},
"mailingAddress": {},
"mailingAddressIsDifferent": false,
"ftinNotLegallyRequired": false
}
}Self-Certification — Individual
{
"accountHolder": {
"isIndividual": true,
"name": "Ray Holt",
"address": {
"firstLine": "100 Harbour Road",
"secondLine": "",
"city": "Dublin",
"stateOrProvince": "",
"postalCode": "D02",
"country": "IE"
},
"countryOfCitizenship": "IE",
"dateOfBirth": "05/31/1994",
"taxResidences": [
{ "country": "IE", "tin": "2342344T" }
]
}
}Self-Certification — Managed Investment Entity
Controlling-person data cannot be pre-populated at this time.
{
"accountHolder": {
"isIndividual": false,
"selfCertificationAccountType": "FINANCIAL_INSTITUTION",
"financialInstitutionType": "INVESTMENT_ENTITY",
"investmentEntityManaged": true,
"entityType": "TRUST",
"name": "Managed Investments Ltd.",
"address": {
"firstLine": "100 Harbour Road",
"secondLine": "",
"city": "Dublin",
"stateOrProvince": "",
"postalCode": "D02",
"country": "IE"
},
"countryOfCitizenship": "IE",
"taxResidences": [
{ "country": "IE", "tin": "2342344T" },
{ "country": "AU", "tin": "51824753556" }
]
}
}Skip server-side prefill
By default, <TaxbitQuestionnaire /> fetches the account owner's prior submission on mount and prefills the form with it, so a returning user lands on a completed summary. For most products that's what you want — it's the same saved data Adaptive mode compares against. For stricter security postures, the prepopulateWithSavedData prop opts out of that fetch.
Setting prepopulateWithSavedData={false}:
- Skips the
GET /tax-documentation/submissionscall entirely — no prior PII is sent over the wire on mount. (The SDK normally appends?unmask=true&questionnaire=…to this same path; with the prop disabled, the request isn't made at all.) - Forces the form to start on step one — the user walks the questionnaire from the beginning, even if they previously submitted a completed form.
- Leaves the status fetch unaffected —
useTaxbit().statusDatastill works exactly as before; status, issues, expiration, and curing signals are all still available. - Leaves submission unaffected —
POST /tax-documentation/submissionsworks the same. Submitting populates the user's record as a fresh submission.
Omitting the prop preserves today's behavior for every existing integration — this is an opt-in change.
| Value | Effect |
|---|---|
prepopulateWithSavedData={true} (default) | Fetches the prior submission, prefills the form, and routes returning users to the summary. |
prepopulateWithSavedData={false} | Doesn't fetch the prior submission. The form starts fresh on step one. |
When to use it
Set prepopulateWithSavedData={false} when:
- You gate the SDK behind step-up auth (2FA, recent-auth) and don't want previously collected data rendered into the DOM during the session.
- Your security posture requires the SDK to display only data the user enters in the current authenticated session.
- The user is expected to re-attest from scratch — compliance-driven re-collection or periodic reverification.
Leave it at the default for the standard onboarding pattern, where returning users review their prior submission and continue from where they left off.
Usage
<TaxbitQuestionnaire
questionnaire="W-FORM"
bearerToken={accountOwnerToken}
prepopulateWithSavedData={false}
onError={(err) => reportToSentry(err)}
onSuccess={() => router.push('/done')}
/>That's the whole integration — no other props or callbacks change because of this flag.
Interaction with other props
data (host-supplied prefill). This prop gates server-saved data only — the submission Taxbit has on file. It does not suppress prefill you pass via data. With prepopulateWithSavedData={false} and a populated data prop, the SDK skips the server fetch but still prefills from data. For a fully blank form, omit data.
adaptiveMode. Adaptive mode compares submitted state against the host-supplied data or the server-fetched submission. With the server side turned off, it has only data to work from: no data means nothing to compare against, so the user sees every field (same as adaptiveMode='full'); a populated data means it skips or locks those fields only. If your goal is "no prior PII," Adaptive mode against a data prop you control is still consistent with that.
useTaxbit. statusData is unaffected — it's metadata from a different endpoint, not PII. But serverData returns an empty object when prepopulateWithSavedData={false}, and refreshSubmission() is a no-op since there's nothing fetched to refresh. Account for the empty case if any UI outside the SDK reads serverData.
Pitfalls
- The prop name means server-saved data, not all prefill. It does not suppress the
dataprop. If a security review hinges on "no prefill at all," set the flag and omit (or audit) what you pass todata. - The prior submission still exists server-side. This flag changes only what the SDK fetches and renders — it doesn't delete or hide prior data on the Taxbit side. A token capable of rendering the SDK can still retrieve the prior submission via a direct API call.
Handle validation and status
TIN matching with the IRS, VAT validation via VIES, and downstream changes all resolve after the user submits.
Received vs. validated
onSuccess is the callback the SDK fires once a submission reaches Taxbit and is accepted. It confirms receipt — not that the TIN matched, the VAT validated, or the form is final.
Validation continues after the user leaves:
- W-9 — TIN matched against the IRS
- DPS — VAT validated against VIES
Either can take minutes to hours, so don't mark a form "complete" on onSuccess alone. Read the final result from tinStatus / vatStatus, covered next.
Read status with the useTaxbit hook
import { useTaxbit } from '@taxbit/react-sdk';
const { statusData } = useTaxbit({ bearerToken, questionnaire: 'W-FORM' });
if (statusData?.wFormQuestionnaire?.tinStatus === 'PENDING') {
return <Badge>Validation in progress</Badge>;
}Both fields begin PENDING and resolve after submission:
tinStatus(W-9, IRS match) —MISMATCHandINVALID_DATAfeed a B-notice flow; aVALID_*_MATCHclears it.realTimeTinValidationattempts the match at submission and reflects it inline.vatStatus(DPS, VIES) — resolves toVALID,INVALID,INSUFFICIENT_DATA,NOT_REQUIRED, orNON_EU.
See the reference for every value.
Respond to changes with webhooks
Submissions can change state long after the user is gone — a W-8 may need resubmission after a change in circumstances, a TIN may flip from VALID to MISMATCH on a re-check, an issue may be cured in the Taxbit Dashboard. These surface as webhook events.
Use the Webhooks Guide for the full event taxonomy. A few notes specific to SDK integrations:
- The account-owner status webhook fires for any change to the status object, not just user-initiated submissions — don't trigger user-facing notifications directly from it.
- To detect a genuinely new submission, compare the document ID from the status API: a changed ID means new data; the same ID means existing data was re-evaluated.
- Delivery retries; if it ultimately fails, file a support ticket to redrive events, and keep your own monitoring on top of Taxbit's.
When needsResubmission is set, route the user back to the same component with adaptiveMode="skipEdit".
Cure post-submission issues
Some W-8 validation issues can be resolved without re-collecting the whole form. When statusData surfaces an OPEN curable issue, route the user to <TaxbitCuringDocumentation /> instead of the full questionnaire — it gathers only the evidence and reasonable explanation needed to clear the open issue.
import { useTaxbit, TaxbitCuringDocumentation } from '@taxbit/react-sdk';
const { needsCuringDocumentation, isLoading } =
useTaxbit({ bearerToken, questionnaire: 'W-FORM' });
if (!isLoading && needsCuringDocumentation) {
return <TaxbitCuringDocumentation bearerToken={bearerToken} onSuccess={refresh} />;
}Gate the mount on needsCuringDocumentation. Curing is W-8 only — W-9 users with issues go back through <TaxbitQuestionnaire /> with adaptiveMode="skipEdit". See the Curing Integration Guide for the full walkthrough: when to use it, curable issue types, what the user sees, and the post-submission lifecycle.
Offer a PDF download on the confirmation screen
This sits at the end of the flow — the confirmation screen you render after onSuccess, not your styling layer. Available for W-Form and Self-Certification submissions, not DPS.
useTaxbit mints a short-lived download URL; gate the link on canGetDocumentUrl.
const { canGetDocumentUrl, generateDocumentUrl, documentUrl } =
useTaxbit({ bearerToken, questionnaire });
useEffect(() => { if (canGetDocumentUrl) generateDocumentUrl(); }, [canGetDocumentUrl]);
return documentUrl ? <a href={documentUrl} download>Download form</a> : null;Customize and ship
Style it to your brand
Semantic HTML with namespaced classes. Import a stylesheet and override what you need.
import '@taxbit/react-sdk/style/inline.css'; // or 'basic.css' / 'minimal.css'.taxbit-button { background: #0B1B33; border-radius: 8px; }
.taxbit-input:focus { outline: 2px solid #2EE5C8; }Support mobile
There's no native SDK — render in a webview and bridge callbacks to native code, reusing the same token flow. Host a page that posts each callback to whichever bridge is present:
function notifyNative(event, payload) {
const msg = JSON.stringify({ event, payload });
window.ReactNativeWebView?.postMessage(msg); // React Native
window.webkit?.messageHandlers?.taxbit?.postMessage(msg); // iOS WKWebView
window.TaxbitBridge?.postMessage(msg); // Android interface
}
<TaxbitQuestionnaire
questionnaire="W-FORM"
bearerToken={bearerToken}
onSuccess={(d) => notifyNative('success', d)}
onError={(e) => notifyNative('error', e)}
/>Load that page in your platform's webview:
- React Native —
react-native-webview - iOS —
WKWebView, withWKUserContentControllermessage handlers - Android —
android.webkit.WebView, with a JavaScript interface
For production:
- Pre-warm the webview off-screen
- Enable DOM storage
- Handle load errors with a native offline screen
- Set the viewport meta tag:
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">Languages
Set language to pre-select the locale; account owners can also switch via an in-form dropdown (hideable with CSS). The language prop accepts the full Locale set (53 tags); the in-form picker shows a subset — roughly 45 for DPS and Self-Certification and 19 for W-Form. See Supported languages for the full list of locale codes.
Next steps
- Component & Hook Reference — every prop, hook return value, and type
- Webhooks Guide — full event taxonomy
- Tax Documentation FAQ and Remediation FAQ — handling W-8/9 issues

