API 1.0.2
Released September 13, 2026
This release adds a first-party integration with Taxi. Enterprise organizations that connect Taxi under Settings → Taxi get client uploads and signed documents pushed into the matching Taxi customer automatically, without granting anyone read access to Zapa.
There are no breaking changes. Existing integrations, webhooks, and Zapier Zaps are unaffected.
Highlights
Taxi file push
For organizations with the Taxi integration connected, setting a portal's externalSystemIds.taxi to a
6-digit Taxi customer id links the portal. Zapa verifies the id against Taxi, then delivers:
- every uploaded file (
file.uploaded) and every fully signed PDF (file.signed) — the bytes are uploaded by Zapa to a short-lived upload URL issued by Taxi, with a SHA-256 checksum that Taxi's storage enforces; task.created,task.completed, andportal.workflow_changedas metadata;- a
portal.unlinkednotice when the id is cleared.
Delivery is at-least-once, idempotent on (file_id, event) so Taxi can deduplicate: transient failures
retry with exponential backoff for 24 hours, after which the file is marked failed in the portal with a
Resend action. Every push is written to
the portal's audit history and triggers guest download notifications, like a member download.
Linking through the REST API works the same as in the web app:
curl -X PATCH https://api.zapaportal.com/api/v1/portals/PORTAL_ID \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "externalSystemIds": { "taxi": "000123" } }'
The id must match ^[0-9]{6}$; anything else is rejected with 400. A taxi id already assigned to
another portal in the organization returns 409 Conflict, as for any external system id.
Saving the id — from this endpoint or the web app — triggers link verification with Taxi. If the link is later found stale (for example the organization connected its Taxi credential after linking portals), Zapa re-verifies automatically the next time it matters: when a file event arrives for the portal, or when the portal page is opened.
External system names are case-insensitive
externalSystemIds keys are now matched ignoring case across the API: taxi, Taxi and TAXI
name the same system. Consequences:
PATCHwith{ "taxi": "…" }replaces an existingTaxientry rather than adding a second key, and{ "taxi": null }removes it. The spelling from the request is kept.- A request whose body contains two keys that differ only by case returns
400. GET /api/v1/portals?externalSystem=Taxi&externalId=…matches portals stored undertaxi, and the409 Conflictuniqueness check compares names case-insensitively.- Organizations cannot register two external system labels that differ only by case.
taxi_link on portal responses
GET /api/v1/portals and GET /api/v1/portals/{portalId} now include a read-only taxi_link
object when the portal has (or had) a taxi external system id:
"taxi_link": {
"status": "linked",
"tenant_id": "firm-abc",
"customer_id": "000123",
"display_name": "Acme Holdings LLC",
"verified_at": "2026-09-13T20:41:07.000Z"
}
status is one of verifying, linked, not_found, credential_rejected, error, or unlinked;
when it is not linked, error carries the reason. The field cannot be set through the API — PATCH
requests that include it are ignored for that key. Poll it after setting the id if you need to confirm
the link before relying on pushes.
Web app
- Settings → Taxi (administrators): connect, replace, or disable the Taxi credential. The pair is verified against Taxi before it is stored; only the last four characters of the key are shown afterwards.
- Portal → Settings → External System IDs: the
taxifield validates the 6-digit format, shows the verified customer name, verifies automatically on save and on page load when the link is stale, and offers Re-verify. - File list: members with Manage Permissions see Sent to Taxi / Sending to Taxi / Taxi failed on each file of a linked portal, with Resend on failures.
See the Taxi Integration admin guide for setup and troubleshooting.
Notes for integrators
- Webhook receivers already get
external_system_idson every payload (since 1.0.1), so a Taxi-linked portal's events carry"external_system_ids": { "taxi": "000123" }. Nothing about webhook payloads or signatures changed in this release. - The Taxi push runs alongside your webhooks, not instead of them; both fire for the same event.
- The Zapier integration is unchanged (still 1.0.1).