Dashboard
Determination Volume
Transactions Created
Recent Activity
| Timestamp | Action | User | Detail | Status |
|---|
Transactions
| Transaction Code i | Description i | Category i | Supply Type i | Confidence i | Status i | Actions i |
|---|
Determination History
| Determination ID i | Transaction ID i | Date | Lines | Net Total | Tax Total | Currency | Source |
|---|
Compliance
| Entity | Jurisdiction | Registration Number | Type | Effective From | Status | Actions |
|---|
| Jurisdiction | Tax Type | Frequency | Due Rule | Actions |
|---|
Settings
ERP Tax Code Mappings i
| Jurisdiction | Tax Treatment | Supply Type | ERP Tax Code | Actions |
|---|
API Tester
Test your saved transactions against any ERP format — see the exact API request & response.
/v1/tax/determine
Header: X-API-Key: your_api_key
Select a saved transaction and ERP system, then click "Build & Run" to see the API response.
API Integration Guide
Everything you need to integrate DeterminedAI into your application or ERP system.
Go to Account and click Generate API Key. Copy it immediately — it's only shown once.
Go to Transactions and describe your products/services. Our AI classifies them for tax treatment.
Send a POST request with your transaction details. We return the tax determination in milliseconds.
DeterminedAI accepts two credential types on every data endpoint. Your integrations should use Option A.
Send the key in the X-API-Key header. This is what every ERP connector uses.
curl -X POST https://determinedai.co/v1/tax/determine \
-H "Content-Type: application/json" \
-H "X-API-Key: txai_live_your_key_here" \
-d '{ ... }'
The dashboard you're looking at uses this path. You do not need to send this from your own code.
Authorization: Bearer <supabase_session_jwt>
Your API key starts with txai_live_. Keep it secret — never ship it in browser JavaScript, mobile apps, or public repos. If it leaks, rotate it immediately.
Every endpoint returns a standard HTTP status. Use these to decide whether to retry, surface an error, or ask the user to re-authenticate.
| Status | Meaning | Retryable? |
|---|---|---|
| 200 OK | Determination succeeded. Parse line_results[]. |
— |
| 400 Bad Request | Payload failed validation (missing required field, bad ISO country, malformed amount). Response body has details. | No — fix the request. |
| 401 Unauthorized | No X-API-Key header and no Authorization: Bearer token. You sent a request with no credentials. |
No — add the header. |
| 403 Forbidden | The API key you sent is not recognized, has been revoked, or the Bearer JWT is invalid/expired. Check the key in your ERP plugin matches your dashboard. | No — rotate or re-paste the key. |
| 409 Conflict | Only on POST /v1/account/api-key: you already have an active key. Use the rotate endpoint to cycle it. |
No — use rotate. |
| 429 Too Many Requests | Monthly plan quota exceeded (500 on Starter, 25k on Professional, 250k on Scale). Response body: "Monthly API quota exceeded (N/M calls)…" |
No — upgrade plan or wait for next month. |
| 500 Server Error | Unexpected error. Capture the response body and contact support with the determination_id if one was returned. |
Yes, with exponential backoff. |
| 503 Service Unavailable | Authentication or account database is temporarily unreachable. This is a transient infrastructure issue — we fail closed rather than silently accept unverified keys. | Yes — retry after 1-5s with backoff. |
Connector behavior: the NetSuite SuiteScript plug-in will flag the transaction with custbody_taxai_needs_review = true on any non-200 response and optionally apply a zero-rate fallback (if custscript_taxai_fallback is enabled), so bookkeeping is never blocked by an API outage — but the line is queued for human review.
Base URL: https://determinedai.co
| Method | Endpoint | Description |
|---|---|---|
| POST | /v1/tax/determine |
Raw tax determination — universal format |
| POST | /v1/erp/netsuite/calculate |
NetSuite SuiteTax native format |
| POST | /v1/erp/xero/calculate |
Xero accounting format (ACCREC / ACCPAY) |
| POST | /v1/erp/quickbooks/calculate |
QuickBooks Online format |
| POST | /v1/erp/dynamics365/calculate |
Dynamics 365 Finance & SCM format |
| POST | /v1/erp/sap/calculate |
SAP S/4HANA & Business One format |
| POST | /v1/erp/sage-intacct/calculate |
Sage Intacct format |
| POST | /v1/tax/chat |
Conversational tax assistant |
| GET | /v1/setup/catalog |
Retrieve your saved transaction catalog |
| POST | /v1/setup/erp-mapping |
Create ERP tax code mappings |
| GET | /v1/setup/erp-mapping |
Retrieve current ERP mappings |
| GET | /v1/setup/erp-mapping/defaults/{erp} |
Get default mappings for a specific ERP |
| POST | /v1/tax/batch-upload |
Batch upload transactions (CSV or free-text) |
| POST | /v1/compliance/registrations |
Add a VAT/GST registration |
| GET | /v1/compliance/registrations |
List all VAT/GST registrations |
| POST | /v1/compliance/filing-rules |
Add a filing rule (deadline schedule) |
| GET | /v1/compliance/filing-rules |
List filing rules and deadlines |
| GET | /v1/account/me |
Current user, plan, usage, key prefix (Bearer JWT only) |
| POST | /v1/account/api-key |
Generate a new API key (returned once — store immediately) |
| POST | /v1/account/api-key/rotate |
Invalidate the current key and issue a new one |
/v1/tax/determine)The universal endpoint. Send transaction details and get back the full VAT/GST determination with audit trail.
POST /v1/tax/determine
Content-Type: application/json
X-API-Key: txai_live_your_key
{
"transaction_id": "INV-2024-001",
"transaction_type": "sale",
"transaction_date": "2024-03-15",
"direction": "AR",
"seller": {
"entity_id": "seller-001",
"name": "Acme SaaS Inc.",
"country": "US",
"tax_registrations": [{
"jurisdiction": "DE",
"tax_id": "DE123456789",
"scheme": "VAT"
}]
},
"customer": {
"customer_id": "cust-42",
"country": "DE"
},
"line_items": [{
"line_id": "line-1",
"description": "Cloud SaaS subscription",
"amount": "99.00",
"currency": "EUR",
"product_code": "SAAS-001"
}]
}
{
"transaction_id": "INV-2024-001",
"determination_id": "det_abc123...",
"timestamp": "2024-03-15T10:30:00Z",
"line_results": [{
"line_id": "line-1",
"supply_type": "electronically_supplied_service",
"tax_treatment": "standard",
"rate": 0.19,
"tax_amount": 18.81,
"net_amount": 99.00,
"jurisdiction": "DE",
"place_of_supply": "DE",
"gl_tax_code": "VAT-DE-STD-19",
"confidence_score": 0.97,
"rule_reference":
"EU VAT Dir. Art. 58",
"characterization_reasoning":
"B2C ESS — tax at customer loc",
"user_obligation":
"Charge 19% DE VAT via OSS"
}],
"total_tax": 18.81,
"total_net": 99.00,
"currency": "EUR",
"warnings": []
}
seller.country (required) — ISO 2-letter code
customer.country (required) — ISO 2-letter code
seller.entity_id, .name (required)
customer.tax_id (optional) — presence = B2B
line_items[].description (required) — drives AI classification
line_items[].incoterm (optional) — DDP, FOB, CIF, etc. for goods
direction (default: AR) — AR (Accounts Receivable) = selling, AP (Accounts Payable) = buying
line_items[].product_code (optional) — matches catalog for instant lookup
rate — decimal (0.19 = 19%), not percentage
tax_treatment — standard | reduced | zero_rated | exempt | reverse_charge | outside_scope
place_of_supply — where tax is due (ISO country)
user_obligation — plain-English action required
rule_reference — legal basis (e.g. "EU VAT Dir. Art. 58")
gl_tax_code — suggested GL posting code
self_assess_rate — for AP reverse charge (buyer self-assesses)
erp_tax_code — your ERP-specific tax code (if mappings configured)
Each ERP endpoint accepts payloads in that system's native format — no translation layer needed. Responses include ERP-native tax codes, rule_reference (legal basis for audit defense), and ready-to-post structures.
POST /v1/erp/netsuite/calculate
subsidiary_country, ship_to.country, lines[].item_id, lines[].amountOptional:
entity_tax_id (B2B), incoterm, lines[].item_type (Service, InvtPart)Returns:
tax_details[] with tax_code, tax_rate (%), tax_amount, jurisdiction, rule_reference
POST /v1/erp/xero/calculate
contact_country, line_items[].description, line_items[].unit_amountOptional:
invoice_type (ACCREC/ACCPAY), contact_tax_number, seller_country (default: GB)Returns:
line_items[] with tax_type (OUTPUT2, ZERORATEDOUTPUT, etc.), rule_reference + ready-to-POST invoice_data
POST /v1/erp/quickbooks/calculate
bill_country, line_items[].amountOptional:
company_country (default: US), customer_tax_id, ship_country, bill_state/ship_stateReturns:
line_tax_details[] with tax_code_ref (TAX/NON), rule_reference + ready-to-write txn_tax_detail
POST /v1/erp/dynamics365/calculate
company_country, party_country, ship_to.country, lines[].amountOptional:
party_tax_registration, document_type (SalesOrder/PurchaseOrder), party_tax_groupReturns:
lines[].tax_components[] with tax_code, tax_rate, jurisdiction, rule_reference + tax_summary[]
POST /v1/erp/sap/calculate
company_country, ship_to.country, lines[].line_number, lines[].net_amountOptional:
customer_vat_number, tax_procedure (TAXD, TAXUSJ), source_system (S4HANA/B1), plantReturns:
lines[].conditions[] with condition_type (MWST), tax_rate, jurisdiction_code, rule_reference + bapiret2[] messages
POST /v1/erp/sage-intacct/calculate
ship_to_country, lines[].amountOptional:
company_country (default: US), customer_tax_id, tax_solution_idReturns:
tax_entries[] with detail_id, tax_authority, tax_rate (%), tax_amount, taxable_amount, rule_reference
Each ERP stores the DeterminedAI API key in its own native secret/config store. You paste it once and the connector sends it on every outgoing call as X-API-Key.
Upload TaxAI_SuiteTax_Plugin.js as a SuiteTax Plug-in, then set these script parameters on the deployment record:
custscript_taxai_api_url = https://determinedai.co
custscript_taxai_api_key = txai_live_xxxxxxxx...
custscript_taxai_timeout = 15000
custscript_taxai_fallback = T # zero-rate if API down
Full walkthrough: connectors/netsuite/SETUP.md in the integration bundle.
Instantiate XeroTaxMiddleware in your backend with the key as a constructor arg — ideally loaded from an env var, never hardcoded:
middleware = XeroTaxMiddleware( taxai_api_url=os.environ["TAXAI_API_URL"], taxai_api_key=os.environ["TAXAI_API_KEY"], xero_client_id=os.environ["XERO_CLIENT_ID"], xero_client_secret=os.environ["XERO_CLIENT_SECRET"], )
Full walkthrough: connectors/xero/SETUP.md.
These ERPs don't have a drop-in plugin bundle — your backend calls the /v1/erp/<erp>/calculate endpoint directly from whichever webhook or event handler creates invoices/bills in that system. Send the key in X-API-Key:
POST /v1/erp/quickbooks/calculate
X-API-Key: txai_live_xxxxxxxx...
Content-Type: application/json
# See Code Examples below for a full request
Store the key in your backend's secret manager (AWS Secrets Manager, GCP Secret Manager, Vercel environment variables, etc.) — never in source control.
Before wiring up real invoices, smoke-test your key against a simple determination:
curl https://determinedai.co/v1/tax/determine \
-H "X-API-Key: $TAXAI_KEY" \
-H "Content-Type: application/json" \
-d '{"transaction_id":"test-1",
"transaction_type":"sale",
"direction":"AR",
"seller":{"entity_id":"s","name":"T","country":"US"},
"customer":{"country":"DE"},
"line_items":[{"line_id":"1",
"description":"SaaS subscription",
"amount":"99.00","currency":"EUR"}]}'
A 200 with line_results means your key is live. 403 means re-copy the key from the Account page; 503 means retry in a few seconds.
Each ERP speaks a different auth model, a different tax data shape, and a different deployment story. Pick your system from the dropdown to see the step-by-step guide for that integration.
- QBO International (UK, CA, AU, IE, ZA, Global) — fully supported, manual tax codes
- QBO-US legacy manual sales tax — supported
- QBO-US with Automated Sales Tax (AST) — not supported. The middleware detects AST at init and raises
QBOUnsupportedConfigError; catch this in your onboarding flow and block the integration.
- Sign in at developer.intuit.com and go to My Apps → Create an app
- Pick QuickBooks Online and Payments
- Under Scopes, enable
com.intuit.quickbooks.accounting - Add your OAuth2 redirect URI and copy your Client ID + Client Secret from Keys & credentials
Use Intuit's OAuth 2.0 Playground or your own server to walk the user through authorization. You will end up with four values: realm_id, access_token (~1h), refresh_token (~100d, rotates on use), and expires_in. Persist all four in your secrets store.
pip install httpx
from qbo_middleware import QBOTaxMiddleware, QBOUnsupportedConfigError
middleware = QBOTaxMiddleware(
taxai_url="https://determinedai.co",
taxai_api_key="txai_live_...",
qbo_client_id="YOUR_INTUIT_CLIENT_ID",
qbo_client_secret="YOUR_INTUIT_CLIENT_SECRET",
environment="production", # or "sandbox"
)
# This probes CompanyInfo + Preferences; raises
# QBOUnsupportedConfigError if the org is QBO-US with AST on.
try:
await middleware.set_tokens(
realm_id=stored["realm_id"],
access_token=stored["access_token"],
refresh_token=stored["refresh_token"],
expires_in=stored["expires_in"],
)
except QBOUnsupportedConfigError as e:
return error_response(str(e))
result = await middleware.create_invoice_with_tax(
doc_number="INV-2024-042",
customer_ref="42",
customer_name="Acme GmbH",
bill_country="DE",
customer_tax_id="DE123456789", # B2B -> EU reverse charge
currency_code="EUR",
line_items=[{
"description": "Cloud analytics - annual SaaS",
"amount": 1188.00,
"quantity": 1,
"item_ref": "SAAS-001",
}],
)
invoice_id = result["Invoice"]["Id"]
QBO TaxCode numeric IDs are company-specific. On set_tokens() the middleware caches the name → id lookup for the org's active tax codes. If your company's tax code names don't match what DeterminedAI returns, configure the mapping via POST /v1/setup/erp-mapping with erp_system: "quickbooks".
See connectors/quickbooks/SETUP.md for the complete guide including AST detection internals, error handling, production checklist, and the live sandbox validation notes.
import requests
resp = requests.post(
"https://determinedai.co/v1/tax/determine",
headers={
"X-API-Key": "txai_live_your_key",
"Content-Type": "application/json",
},
json={
"transaction_id": "INV-001",
"transaction_type": "sale",
"direction": "AR",
"seller": {
"entity_id": "s1",
"name": "My Co",
"country": "US"
},
"customer": {"country": "DE"},
"line_items": [{
"line_id": "1",
"description": "SaaS subscription",
"amount": "99.00",
"currency": "EUR"
}]
}
)
data = resp.json()
line = data["line_results"][0]
print(f"Rate: {line['rate']}") # 0.19
print(f"Tax: {line['tax_amount']}") # 18.81
print(f"Code: {line['gl_tax_code']}")# VAT-DE-STD-19
const resp = await fetch(
"https://determinedai.co/v1/tax/determine",
{
method: "POST",
headers: {
"X-API-Key": "txai_live_your_key",
"Content-Type": "application/json",
},
body: JSON.stringify({
transaction_id: "INV-001",
transaction_type: "sale",
direction: "AR",
seller: {
entity_id: "s1",
name: "My Co",
country: "US",
},
customer: { country: "DE" },
line_items: [{
line_id: "1",
description: "SaaS subscription",
amount: "99.00",
currency: "EUR",
}],
}),
}
);
const data = await resp.json();
const line = data.line_results[0];
console.log(line.rate); // 0.19
console.log(line.tax_amount); // 18.81
console.log(line.gl_tax_code); // VAT-DE-STD-19
Your API key is the identity your ERPs use to authenticate with DeterminedAI. Manage it from the Account page or directly via the API.
Creates your first key. Returns the plaintext exactly once — copy it before closing the dialog.
POST /v1/account/api-key
Authorization: Bearer <session_jwt>
# Response (once, never again)
{
"api_key": "txai_live_xxxxxxxxxxxxxxxx",
"prefix": "xxxxxxxx",
"message": "Store this key securely..."
}
Use this if your key is compromised, if an employee with key access leaves, or on a scheduled rotation. The old key stops working immediately.
POST /v1/account/api-key/rotate
Authorization: Bearer <session_jwt>
# Response
{
"api_key": "txai_live_yyyyyyyyyyyyyyyy",
"prefix": "yyyyyyyy",
"message": "Old key has been invalidated..."
}
- Format:
txai_live_prefix + 32 URL-safe base64 characters. Total length ~42 chars. - Storage: we store only a SHA-256 hash — we cannot recover or display your plaintext key after generation. If you lose it, rotate.
- One active key per account. Calling generate while a key exists returns
409 Conflict; use rotate instead. - Identity: the same key works across every ERP connector, the universal
/v1/tax/determineendpoint, and any custom scripts. There's no per-ERP key. - Dashboard key preview shows only the first 8 characters (e.g.
txai_live_aBcDeFgH••••••••). The full key is never re-displayed after generation. - Compromised? Rotate immediately. There is no grace period — after rotation, any request with the old key returns
403 Forbidden.
| Plan | API Calls / Month | ERP Integrations | Transactions |
|---|---|---|---|
| Starter (Free) | 500 | 1 ERP | 50 |
| Professional ($249/mo) | 25,000 | 2 ERPs | Unlimited |
| Scale ($699/mo) | 250,000 | All ERPs | Unlimited |
API calls are counted per /v1/tax/determine (or equivalent ERP endpoint) request. Quotas reset on the 1st of each month UTC. Overage returns 429 Too Many Requests with a message telling you your current count and the plan ceiling — upgrade from the Account page to resume immediately.
Use the Help chat in the bottom-right corner, or reach out via Contact. We're happy to help with your integration.
Account
txai_live_••••••••••••••••
| Plan | API Calls / Month | Transactions | ERPs | History | Support | Price | |
|---|---|---|---|---|---|---|---|
| Starter | 500 | 50 | 1 | 30 days | Community | $0 | Current |
| Professional | 25,000 | Unlimited | 2 | 1 year | $249/mo | ||
| Scale | 250,000 | Unlimited | All | Full + export | Priority | $699/mo | |
| Enterprise | Unlimited | Unlimited | All + custom | Full + export | Dedicated + SLA | Custom | Contact |
Separate from your Tax Engine subscription. Your current OSS status: No OSS services purchased.
| Service | What you get | Billing | Price | |
|---|---|---|---|---|
| Registration | Non-Union OSS registration with Revenue, ROS cert capture, first return included | One-time | $1,000 | |
| Quarterly Return | Draft, review, file a single quarter. ECB FX + Excel work product included. | One-time | $250 | |
| Unlimited Filing | All 4 quarters, ROS automation, cert expiry alerts, amendment support | $799 / year | $799 |
Contact Us
Looking to replace your current tax engine? We offer migration support from Avalara, Vertex, and other providers. Contact us for a custom quote and demo tailored to your ERP setup.
Exposure Snapshots
Every time you calculate your global VAT/GST exposure on the Exposure tool while signed in, we save a snapshot here so you can revisit and compare runs over time. Paid plans only.
Saved snapshots
+ New snapshotRegistrations & Returns
Non-Union OSS registration + automated quarterly filing for all 27 EU member states.
My registrations
Already registered for Non-Union OSS with Revenue? Import your existing registration — upload your ROS Digital Certificate and we'll take over quarterly filing without the wizard.
Legal
Terms of Service
Last Updated: April 2026
1. Service Description
DeterminedAI provides an AI-powered VAT/GST tax determination engine ("Service") that classifies transactions, determines applicable tax rates, and returns tax calculations in ERP-native formats.
2. Client Responsibilities
The Client acknowledges that it bears the ultimate legal responsibility for the correctness of its tax filings and payments to tax authorities, regardless of the calculations provided by the Service.
The Client is responsible for:
- Reviewing and approving AI-generated transaction classifications before production use
- Maintaining accurate VAT/GST registration data
- Maintaining accurate VAT/GST registration details for all jurisdictions
- Verifying tax calculations are correctly applied in their ERP system
3. Limitation of Liability
DeterminedAI's total aggregate liability shall not exceed the fees paid by the Client during the twelve (12) months preceding the claim. DeterminedAI shall not be liable for any tax penalties, interest, or additional assessments imposed by tax authorities.
4. AI Classification Disclaimer
Tax classifications generated by the Service's AI engine are recommendations based on the information provided. The approval workflow serves as the Client's explicit review and acceptance of these classifications. Once approved, the Client assumes responsibility for the accuracy of the classification.
5. Data Retention
Tax determination records are retained for the duration of the Client's subscription. Upon termination, the Client has 30 days to export all data. Following the export period, Client Data is deleted in accordance with applicable law.
For the full Terms of Service, contact jbburns@determinedai.co
Privacy Policy
Last Updated: April 2026
Information We Collect
To perform tax determinations, we process:
- Transaction data (seller/customer details, line items, amounts)
- Transaction data (descriptions, categories, AI classifications)
- Account information (company name, contact email, billing details)
- API usage logs (endpoints, timestamps, response codes)
How We Use Information
We use collected information solely to provide the Service, maintain audit trails, and improve accuracy. We do not use your transaction data to train AI models. Classifications are generated per-request and cached only for your account.
Data Sharing
We do not sell your data. We share information only with infrastructure providers under contractual obligations, when required by law, or with your explicit consent.
Your Rights (GDPR)
For EU/EEA clients: we act as a data processor for transaction data. You may exercise your rights to access, correct, delete, or export your data by contacting us. We respond within 30 days.
Data Retention
| Data Type | Retention |
|---|---|
| Tax determination records | Duration of subscription |
| Transaction catalog data | Duration of subscription + 30 days |
| API request logs | 90 days |
For the full Privacy Policy, contact jbburns@determinedai.co
Data Processing Agreement
Last Updated: April 2026
Roles
The Client is the data controller. DeterminedAI is the data processor, processing personal data solely on behalf of the Client to provide the Service.
Data Processed
- Customer names and business contact details
- Addresses and country codes
- VAT/GST registration numbers
- Transaction details (invoices, amounts, dates)
Security Measures
- Encryption in transit via HTTPS (provided by hosting infrastructure)
- API key authentication for all endpoints
- Database hosted on Supabase with encryption at rest
- Breach notification as required by applicable law
Sub-processors
| Sub-processor | Purpose | Location |
|---|---|---|
| Supabase (AWS) | Database hosting | US East |
| Vercel | Application hosting | Global CDN |
| Anthropic | AI classification engine | US |
International Transfers
Where personal data is transferred outside the EEA, appropriate safeguards are in place through our sub-processors' compliance programs. Contact us for details on specific transfer mechanisms.
For the full DPA, contact jbburns@determinedai.co