SLTax360 REST API

Programmatically submit invoices, manage policies, and access reference data.

Tenant API (Invoices, Policies, Companies)
https://your-tenant.sltax360.com/api/v1
Shared API (Auth, Calculator)
https://api.sltax360.com/api/v1
POST /invoices
GET /invoices
GET /companies
GET /policies
POST /auth/token
POST /calculator/estimate
Documentation

Authentication

All API requests require a Bearer token in the Authorization header. Tokens are issued per-tenant and scoped to your account.

HTTP Header
Authorization: Bearer sltax_t2_your_api_key_here

Token Format

API keys follow the format sltax_t2_<random> where t2 identifies the token version.

One Key, Two Base URLs

Your API key works across both base URLs. Obtain a token from the shared Auth endpoint, then use the same Bearer token for tenant-specific endpoints.

ScopeAccess
readRead invoices, companies, policies, insureds, and reference data
writeCreate and update invoices
calculatorAccess the tax calculator endpoint
Security: Never expose API keys in client-side code, public repositories, or browser requests. Always make API calls from your server.

Base URLs

The SLTax360 API uses two base URLs. Tenant endpoints (invoices, policies, companies, insureds, reference data) are served from your tenant subdomain. Shared endpoints (authentication, tax calculator) are served from the central API domain.

Base URLEndpointsDescription
your-tenant.sltax360.com/api/v1 Invoices, Companies, Policies, Insureds, Reference Data Tenant-specific data (reads/writes your tenant database)
api.sltax360.com/api/v1 Auth, Calculator Shared services (authentication tokens, tax estimation)
Same API key: Your Bearer token is valid on both base URLs. No separate credentials needed.

Quick Start

Submit an invoice to the API with a single request:

cURL
curl -X GET "https://your-tenant.sltax360.com/api/v1/invoices" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here" \
  -H "Content-Type: application/json"
JavaScript (fetch)
const response = await fetch('https://your-tenant.sltax360.com/api/v1/invoices', {
  headers: {
    'Authorization': 'Bearer sltax_t2_your_api_key_here',
    'Content-Type': 'application/json',
  },
});

const result = await response.json();

if (result.success) {
  console.log('Invoices:', result.data);
} else {
  console.error('Error:', result.error.message);
}
Python (requests)
import requests

response = requests.get(
    "https://your-tenant.sltax360.com/api/v1/invoices",
    headers={
        "Authorization": "Bearer sltax_t2_your_api_key_here",
        "Content-Type": "application/json",
    },
)

result = response.json()

if result["success"]:
    for inv in result["data"]:
        print(f"Invoice #{inv['id']}: {inv['invoice_number']}")
else:
    print(f"Error: {result['error']['message']}")

Response Format

All responses use a consistent JSON envelope with success, data/error, and meta fields.

Success Response

Success envelope
{
  "success": true,
  "data": { /* endpoint-specific data */ },
  "meta": {
    "timestamp": "2026-01-19T12:00:00Z",
    "request_id": "req_abc123",
    "response_time_ms": 45
  }
}

Error Response

Error envelope
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": { /* field-level errors when applicable */ }
  },
  "meta": { /* same as success */ }
}

Meta Object

FieldDescription
timestampISO 8601 timestamp of the response
request_idUnique request identifier for debugging. Include this in support requests.
response_time_msServer-side processing time in milliseconds

Error Codes

HTTP StatusError CodeDescription
400BAD_REQUESTMalformed request body or parameters
401UNAUTHORIZEDMissing or invalid API key
403FORBIDDENAPI feature not enabled for your plan
404NOT_FOUNDResource or endpoint not found
405METHOD_NOT_ALLOWEDWrong HTTP method for this endpoint
422VALIDATION_ERRORRequest parameters failed validation. Check details.fields.
429RATE_LIMIT_EXCEEDEDToo many requests. Retry after the period in the response.
500INTERNAL_ERRORUnexpected server error. Contact support with request_id.

Validation Error Example

422 Validation Error
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed",
    "details": {
      "fields": {
        "invoice_number": ["is required"],
        "state_id": ["must be a valid state ID"]
      }
    }
  }
}

Rate Limits

API requests are rate-limited per API key on a per-hour basis. Limits vary by plan tier.

Response Headers

HeaderDescription
X-RateLimit-LimitMaximum requests allowed per hour
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the limit resets

Limits by Plan

PlanRequests / Hour
Professional500
Enterprise5,000
Need higher limits? Contact us at support@sltax360.com to discuss custom rate limits for your integration.

Attaching Documents to Invoices

Documents are supporting files (declarations page, SL2 form, Lloyd's syndicates list, policy endorsement, quote, other) that some states require at submission time. Documents are stored in tenant S3 and returned with 60-minute pre-signed URLs.

Two ways to send the bytes

Each document carries its bytes in exactly one of two ways (sending both, or neither, is a 422):

  • base64 — inline bytes. Decoded, mime/size-checked and stored synchronously; the response carries a usable url right away.
  • docUUID — a reference to a file in your external NGIN blob storage. The document is recorded immediately and the bytes are pulled asynchronously from NGIN by a background job. The row starts at fetch_status: "pending" with a null url/file_size and flips to "fetched" once the file lands in S3. When using docUUID, filename and mime_type are optional — NGIN supplies them at fetch time.

Two delivery paths

You can attach documents inline at create by including a documents[] array on POST /invoices — one atomic round trip. Or you can use the post-create sub-resource below at any time, including after state submission. Post-create attachment is the right path when documents arrive late (auditor follow-ups, post-bind endorsements).

Endpoints

MethodPathPurpose
POST /invoices/{id}/documents Append one or more documents
GET /invoices/{id}/documents List documents on an invoice
GET /invoices/{id}/documents/{doc_id} Fetch a single document with a fresh pre-signed URL
PUT /invoices/{id}/documents/{doc_id} Replace bytes and/or update metadata
DELETE /invoices/{id}/documents/{doc_id} Remove a single document

Limits and allowed values

ConstraintValue
Max documents per request10
Max size per document (decoded)10 MB
Max combined size per request25 MB
Allowed type valuesdeclaration_page, sl2_form, lloyds_syndicates, policy_endorsement, quote, other
Allowed mime_type valuesapplication/pdf, image/png, image/jpeg, image/jpg
Permitted after submission. Document attachment is allowed even after the invoice has been submitted to a state (status_id = 3). This is the only post-submit mutation the API allows, and is what lets you attach a dec page returned by an auditor or a post-bind endorsement to an already-filed invoice.

1. Append a document — POST /invoices/{id}/documents

Send one or more documents in a single call. Returns the newly-created rows with 60-minute pre-signed URLs.

cURL — POST request
curl -X POST "https://your-tenant.sltax360.com/api/v1/invoices/12345/documents" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      {
        "type":      "declaration_page",
        "filename":  "dec.pdf",
        "mime_type": "application/pdf",
        "base64":    "JVBERi0xLjQK..."
      }
    ]
  }'
201 Created — response body
{
  "success": true,
  "data": {
    "documents": [
      {
        "id": 501,
        "doc_type": "declaration_page",
        "file_name": "dec.pdf",
        "mime_type": "application/pdf",
        "file_size": 184392,
        "is_excluded": false,
        "external_doc_uuid": null,
        "fetch_status": "none",
        "url": "https://s3.amazonaws.com/...&X-Amz-Expires=3600&...",
        "url_expires_in_seconds": 3600,
        "added": "2026-05-19 09:14:22"
      }
    ]
  }
}

By reference (NGIN docUUID). Send docUUID instead of base64 — no bytes in the request. The document is recorded as pending and pulled from NGIN asynchronously; poll GET until fetch_status is fetched for a usable url.

cURL — POST by reference
curl -X POST "https://your-tenant.sltax360.com/api/v1/invoices/12345/documents" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "documents": [
      {
        "type":    "declaration_page",
        "docUUID": "00788bf-b724-417d-a73b-2d3b5b09a94d"
      }
    ]
  }'

2. List documents — GET /invoices/{id}/documents

Returns every invoice_files row tied to the invoice, regardless of source (api, email, manual). Each item carries a fresh 60-minute pre-signed S3 URL.

cURL — GET list
curl "https://your-tenant.sltax360.com/api/v1/invoices/12345/documents" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here"

3. Get a single document — GET /invoices/{id}/documents/{doc_id}

Use this when a previously-fetched pre-signed URL has expired and you need a fresh one without re-listing every document on the invoice.

cURL — GET single
curl "https://your-tenant.sltax360.com/api/v1/invoices/12345/documents/501" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here"
Do not store the url field. It is a 60-minute pre-signed S3 URL — it will stop working after one hour. Re-fetch via GET /invoices/{id}/documents/{doc_id} whenever you need a fresh URL.

4. Update a document — PUT /invoices/{id}/documents/{doc_id}

The {doc_id} path segment may be either our numeric document id or your NGIN docUUID (for documents you attached by reference). The endpoint supports three modes, selected by which field you send (base64 and docUUID are mutually exclusive). All fields are optional; an empty body returns 422.

4a. Replace the file bytes. When base64 is present, the document is re-uploaded to a new S3 key, the row is updated, and the old S3 object is removed best-effort. You may also send new type, filename, or mime_type in the same call.

cURL — PUT replace bytes
curl -X PUT "https://your-tenant.sltax360.com/api/v1/invoices/12345/documents/501" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "type":      "declaration_page",
    "filename":  "dec-corrected.pdf",
    "mime_type": "application/pdf",
    "base64":    "JVBERi0xLjQK..."
  }'

4b. Update metadata only. Omit base64 and the row is updated in place — no S3 traffic. Send any combination of type, filename, and is_excluded.

cURL — PUT metadata only
curl -X PUT "https://your-tenant.sltax360.com/api/v1/invoices/12345/documents/501" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "type":     "policy_endorsement",
    "filename": "endorsement-2.pdf"
  }'

4c. Re-point a NGIN reference. Send a new docUUID and the document resets to fetch_status: "pending"; any previously fetched file is removed and the background job re-pulls the new document from NGIN. You may also send a new type. Address the document by its current docUUID or numeric id.

cURL — PUT re-point reference
curl -X PUT "https://your-tenant.sltax360.com/api/v1/invoices/12345/documents/00788bf-b724-417d-a73b-2d3b5b09a94d" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "type":    "declaration_page",
    "docUUID": "9f3c1a2e-5d6b-4a7c-8e9f-0a1b2c3d4e5f"
  }'

5. Delete a document — DELETE /invoices/{id}/documents/{doc_id}

Hard-deletes the row and removes the underlying S3 object best-effort. Responds 204 No Content on success.

cURL — DELETE
curl -X DELETE "https://your-tenant.sltax360.com/api/v1/invoices/12345/documents/501" \
  -H "Authorization: Bearer sltax_t2_your_api_key_here"

DocumentResponse shape

The same object is returned by GET, POST, and PUT (and by the documents[] field on GET /invoices/{id}).

FieldTypeDescription
idintegerDocument row id (the numeric {doc_id} path segment)
doc_typestringOne of the allowed type values
file_namestringCaller-supplied filename, or NGIN's filename for references
mime_typestring or nullMime verified against the file's magic bytes; null for a reference until fetched
file_sizeinteger or nullDecoded byte count of the stored file; null until fetched
is_excludedbooleanSoft-exclude flag; documents marked excluded are kept but hidden from internal UI
external_doc_uuidstring or nullYour NGIN docUUID when the document was attached by reference; null otherwise
fetch_statusstringnone (inline base64), or for references: pending/fetching (in flight), fetched (in S3), failed (auto-retried)
urlstring or null60-minute pre-signed S3 URL; null when S3 is not configured or a reference is not yet fetched
url_expires_in_secondsinteger or nullAlways 3600 when url is set
addeddatetimeWhen the document row was created (immutable)
Try it interactively. Every operation above is also available in the Interactive Tenant API Reference below — click Authorize, paste your Bearer token, and use Try it out to fire requests directly from this page.

Interactive Shared API Reference

POST /auth/token · GET /auth/me · POST /calculator/estimate

Authentication and tax calculation endpoints available at the shared base URL.

Base URL: api.sltax360.com/api/v1 — Click Authorize to enter your Bearer token, then Try it out on any endpoint.

Interactive Tenant API Reference

POST /invoices · GET /invoices · GET /companies · GET /policies · GET /insureds

Tenant-specific endpoints for managing invoices, companies, policies, insureds, and reference data.

Base URL: {tenant_slug}.sltax360.com/api/v1 — Click Authorize to enter your Bearer token, then Try it out on any endpoint.