Skip to main content

Zapa Client Portals API (1.0.0)

Download OpenAPI specification:Download

Zapa Client Portals Support: support@zapaportal.com License: Proprietary

REST API for Zapa Client Portals management.

Authentication

All API endpoints require OAuth 2.0 authentication. Include your access token in the Authorization header:

Authorization: Bearer YOUR_ACCESS_TOKEN

Rate Limiting

  • 10,000 requests per day per client
  • Retry with exponential backoff on 429 responses

Webhooks

Subscribe to real-time events by registering webhooks. All webhook payloads are signed with HMAC-SHA256.

Linking Users Into the App

API resources can be turned into web links that open the app directly:

  • Portal: https://app.zapaportal.com/org/{org_id}/vault/{portal_id}/folder/main
  • File preview: append ?file={file_id} to a portal link to open that file's preview automatically

The org_id, portal id, and file id values all come from the API responses below.

File Content Access

The API intentionally provides file metadata only — there is no endpoint that returns file contents or a download URL. Portals typically hold sensitive client documents (tax records, contracts, signed agreements), so downloads are restricted to signed-in users in the web app, where access is permission-checked and recorded in the portal's audit history. Every OAuth consent screen makes this promise to the end user: "This app will not have access to download file contents."

To send someone to a file, use a file preview link (above) rather than trying to fetch the bytes.

Requesting Additional Functionality

If your integration needs a capability this API doesn't offer yet, contact support@zapaportal.com — we prioritize API additions based on customer requests.

OAuth

OAuth 2.0 authentication endpoints

Authorization endpoint

Starts the OAuth 2.0 Authorization Code flow. Direct the user's browser here (on the web app domain, https://app.zapaportal.com) — they'll see a consent screen listing the scopes you requested and can approve or deny each one. After approval, the user is redirected to your redirect_uri with a short-lived code to exchange at the token endpoint.

Always pass a random state value and verify it on the redirect to protect against CSRF.

query Parameters
client_id
required
string
response_type
required
string
Value: "code"
redirect_uri
required
string <uri>
scope
string
state
string

Responses

Token endpoint

Exchanges an authorization code for an access token, or refreshes an expired access token. This endpoint lives on the API domain (https://api.zapaportal.com), unlike the authorize endpoint which is on the web app domain.

Access tokens expire after 1 hour (expires_in: 3600); store the refresh_token and use the refresh_token grant to get a new one without user interaction. The endpoint accepts both application/x-www-form-urlencoded and application/json bodies.

Request Body schema: application/json
required
One of
grant_type
required
string
Value: "authorization_code"
code
required
string
client_id
required
string
client_secret
required
string
redirect_uri
required
string

Responses

Request samples

Content type
application/json
{
  • "grant_type": "refresh_token",
  • "refresh_token": "string",
  • "client_id": "string",
  • "client_secret": "string"
}

Response samples

Content type
application/json
{
  • "access_token": "string",
  • "token_type": "Bearer",
  • "expires_in": 3600,
  • "refresh_token": "string",
  • "scope": "string"
}

User

Authenticated user information

Get authenticated user

Returns the user and organization associated with the access token — user and org IDs and names, the OAuth client ID, and the scopes that were actually granted.

Call this first after obtaining a token: it verifies authentication works, tells you which scopes the user approved on the consent screen (they may have granted fewer than you requested), and gives you the org_id needed to build web links to portals and files. No specific scope is required — any valid token works.

Authorizations:
OAuth2

Responses

Response samples

Content type
application/json
{
  • "user_id": "string",
  • "user_name": "string",
  • "user_email": "string",
  • "org_id": "string",
  • "org_name": "string",
  • "client_id": "string",
  • "scopes": [
    ]
}

Portals

Client portal management

List workflow states

Returns the organization's configured workflow states as {id, name} pairs. Call this before setting a portal's workflow state — the workflow endpoint requires both values, and they must match an existing state. Also handy for populating dropdowns in your integration's UI.

Authorizations:
OAuth2

Responses

Response samples

Content type
application/json
[
  • {
    }
]

List portals

Returns all active (non-deleted) portals in your organization, including tags, workflow state, task counters, and recent-activity timestamps — useful for syncing portal lists into a CRM or dashboard.

Templates: pass templatesOnly=true to list your organization's portal templates instead of regular portals. Use a template's id as the templateId when creating a new portal to copy its folder structure and settings.

Linking: build a web link to any portal with https://app.zapaportal.com/org/{org_id}/vault/{id}/folder/main.

Authorizations:
OAuth2
query Parameters
templatesOnly
boolean
Default: false

When true, returns portal templates instead of regular portals

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Create portal

Creates a new client portal — typically one per client or engagement.

Creating from a template (Business & Enterprise): first call GET /api/v1/portals?templatesOnly=true to find your template's id, then pass it as templateId. The new portal is created with the template's folder structure and default settings:

{ "name": "ABC Corp - 2026 Tax Return", "templateId": "TEMPLATE_UUID", "tags": ["tax-2026"] }

Set isTemplate: true instead to create a new template rather than a regular portal.

Next steps: invite the client with POST /api/v1/portals/{portalId}/guests, then upload files with POST /api/v1/portals/{portalId}/files. Subscribing a webhook to portal.created lets other systems react to portals created here or in the web app.

Authorizations:
OAuth2
Request Body schema: application/json
required
name
required
string

Display name, e.g. client name or project

tags
Array of strings

Tags for filtering and grouping on the dashboard

isTemplate
boolean

Create a reusable template instead of a regular portal

templateId
string <uuid>

ID of a portal template to copy structure and settings from

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "tags": [
    ],
  • "isTemplate": true,
  • "templateId": "196100ac-4eec-4fb6-a7f7-86c8b584771d"
}

Response samples

Content type
application/json
{
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "name": "string",
  • "org_id": "a40f5d1f-d889-42e9-94ea-b9b33585fc6b",
  • "tags": [
    ],
  • "workflowStateId": "string",
  • "workflowStateName": "string",
  • "archived": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by": "string"
}

Get portal by ID

Returns a single portal's details, including tags, workflow state, and activity timestamps. Use this to refresh one portal's state instead of re-listing everything, or to resolve a portal ID received in a webhook payload.

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "name": "string",
  • "org_id": "a40f5d1f-d889-42e9-94ea-b9b33585fc6b",
  • "tags": [
    ],
  • "workflowStateId": "string",
  • "workflowStateName": "string",
  • "archived": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by": "string"
}

Update portal

Updates a portal's name, tags, workflow state, or archive status. Only include the fields you want to change.

Set archived: true to archive a portal when an engagement ends — nothing is deleted, and it can be restored later with archived: false. There is no delete endpoint; portals can only be permanently deleted from the web app.

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>
Request Body schema: application/json
required
name
string
tags
Array of strings
workflowStateId
string
workflowStateName
string
archived
boolean

Responses

Request samples

Content type
application/json
{
  • "name": "string",
  • "tags": [
    ],
  • "workflowStateId": "string",
  • "workflowStateName": "string",
  • "archived": true
}

Response samples

Content type
application/json
{
  • "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
  • "name": "string",
  • "org_id": "a40f5d1f-d889-42e9-94ea-b9b33585fc6b",
  • "tags": [
    ],
  • "workflowStateId": "string",
  • "workflowStateName": "string",
  • "archived": true,
  • "created_at": "2019-08-24T14:15:22Z",
  • "created_by": "string"
}

Set portal workflow state

Moves a portal to a different workflow state (e.g. "In Progress" → "Review"). Fetch valid workflowStateId/workflowStateName pairs from GET /api/v1/workflow-states first — both values are required and should come from that list.

State changes fire the portal.workflow_changed webhook event, making this a good integration point for driving external processes (e.g. notify your practice-management system when a portal reaches "Complete").

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>
Request Body schema: application/json
required
workflowStateId
required
string
workflowStateName
required
string

Responses

Request samples

Content type
application/json
{
  • "workflowStateId": "string",
  • "workflowStateName": "string"
}

Files

File upload and management

List files in portal

Returns metadata (name, size, extension, upload date/user) for every active file and folder in a portal. Folders appear as entries too — a file's parent_folder_file_id links it to its folder.

Linking to a file: to send someone to a specific file, build a web preview link: https://app.zapaportal.com/org/{org_id}/vault/{portalId}/folder/main?file={file_id} — opening it signs the user in (if needed) and opens that file's preview automatically.

Why no download endpoint? The API deliberately never returns file contents. Portal documents are often sensitive (tax records, contracts, signed agreements), and the OAuth consent screen promises users that connected apps cannot download their file contents. Downloads happen in the web app, where they're permission-checked per user and recorded in the portal's audit history. If your integration has a use case that requires content access, contact support@zapaportal.com to discuss it.

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Upload a file

Uploads a file to a portal. Two modes are supported:

One-step (recommended for automation): include a public HTTPS file_url and the server fetches the file (up to 100 MB) and stores it immediately — the response is the completed file record. This is how the Zapier integration uploads files.

Two-step (for uploading local bytes): omit file_url and the response contains a presigned upload_url valid for 1 hour. Then:

  1. PUT the raw file bytes to upload_url, sending the same Content-Type you specified here (the response echoes the exact method and headers to use)
  2. Call POST /api/v1/portals/{portalId}/files/complete with the returned file_id to register the file — until you do, the file will not appear in the portal

Successful uploads fire the file.uploaded webhook event and notify portal users per their notification settings. Pass parent_folder_file_id (a folder's file ID from the list endpoint) to place the file inside a folder.

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>
Request Body schema: application/json
required
filename
required
string

File name including extension, e.g. engagement-letter.pdf

contentType
string

MIME type (defaults to application/octet-stream)

size
integer

File size in bytes (used for the two-step flow's file record)

file_url
string <uri>

Public HTTPS URL to fetch the file from (one-step mode, max 100 MB)

parent_folder_file_id
string <uuid>

Folder to place the file in (omit for the portal root)

Responses

Request samples

Content type
application/json
{
  • "filename": "string",
  • "contentType": "string",
  • "size": 0,
  • "file_url": "http://example.com",
  • "parent_folder_file_id": "0a617e6d-dd07-4f15-a106-b7abf547121c"
}

Response samples

Content type
application/json
{
  • "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767",
  • "upload_url": "http://example.com",
  • "expires_in": 3600,
  • "method": "PUT",
  • "headers": { }
}

Complete a two-step upload

Registers a file after you've PUT its bytes to a presigned upload_url. This creates the file record so it appears in the portal, notifies users, and fires the file.uploaded webhook event.

Only needed for the two-step upload flow — one-step (file_url) uploads are registered automatically.

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>
Request Body schema: application/json
required
file_id
required
string <uuid>

The file_id returned when you requested the upload URL

filename
required
string
extension
string

Defaults to the filename's extension

size
integer

File size in bytes

parent_folder_file_id
string <uuid>

Responses

Request samples

Content type
application/json
{
  • "file_id": "8a0cfb4f-ddc9-436d-91bb-75133c583767",
  • "filename": "string",
  • "extension": "string",
  • "size": 0,
  • "parent_folder_file_id": "0a617e6d-dd07-4f15-a106-b7abf547121c"
}

Response samples

Content type
application/json
{
  • "error": "string",
  • "error_description": "string"
}

Tasks

Task management

List tasks in a portal

Returns all active tasks in a portal with their status, due date, assignees, and type. Useful for syncing outstanding client action items into an external task tracker, or checking whether a client has completed their requested items before advancing the portal's workflow state.

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Create task

Creates a task in a portal — a request for the client (or your team) to do something, like "Upload your W-2" or "Review the engagement letter". Tasks are created with status open; assignees are notified and receive reminders as the due date approaches.

action_type categorizes the request (Question?, File Request, Sign Document, Fill Form, or General). Creating a task fires the task.created webhook event.

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>
Request Body schema: application/json
required
title
required
string
description
string
due_date
string <date-time>
action_type
string
Default: "General"
assigned_guest_ids
Array of strings
assigned_guest_names
Array of strings
assigned_member_ids
Array of strings
assigned_member_names
Array of strings

Responses

Request samples

Content type
application/json
{
  • "title": "string",
  • "description": "string",
  • "due_date": "2019-08-24T14:15:22Z",
  • "action_type": "General",
  • "assigned_guest_ids": [
    ],
  • "assigned_guest_names": [
    ],
  • "assigned_member_ids": [
    ],
  • "assigned_member_names": [
    ]
}

Response samples

Content type
application/json
{
  • "task_id": "736fde4d-9029-4915-8189-01353d6982cb"
}

Update task

Updates one or more fields on a task; omitted fields keep their current values. Task IDs come from the portal's task list endpoint.

You can change status here (e.g. reopen a task by setting a non-done status), but if you just want to finish a task, prefer POST /api/v1/tasks/{taskId}/complete — it also fires the task.completed webhook event.

Authorizations:
OAuth2
path Parameters
taskId
required
string <uuid>
Request Body schema: application/json
required
title
string
description
string
status
string

Use done, completed, or closed to finish a task; anything else counts as open.

due_date
string or null <date-time>

Responses

Request samples

Content type
application/json
{
  • "title": "string",
  • "description": "string",
  • "status": "string",
  • "due_date": "2019-08-24T14:15:22Z"
}

Response samples

Content type
application/json
{
  • "success": true,
  • "task_id": "736fde4d-9029-4915-8189-01353d6982cb"
}

Mark task complete

Marks a task as completed and updates the portal's open/closed task counters. This is the preferred way to finish a task from an integration — for example, auto-completing a "File Request" task after your system uploads the requested document. Fires the task.completed webhook event.

Authorizations:
OAuth2
path Parameters
taskId
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "success": true,
  • "task_id": "736fde4d-9029-4915-8189-01353d6982cb"
}

Guests

Guest invitation

Invite guests to a portal

Invites one or more guests (clients) to a portal by email. Each guest receives an invitation email; new users are prompted to create an account, existing users just accept the invitation. Accepts either singular (email, name) or plural (emails, names) forms.

This is the typical second step after creating a portal: create the portal, invite the client, upload their documents. Invitations fire the guest.invited webhook event.

Guest permissions can't be set through the API yet — new guests get your organization's default portal permissions, adjustable in the web app's Members panel. Contact support@zapaportal.com if your integration needs permission control.

Authorizations:
OAuth2
path Parameters
portalId
required
string <uuid>
Request Body schema: application/json
required
email
string <email>

Single guest email (alternative to emails)

name
string

Single guest name (alternative to names)

emails
Array of strings <email> [ items <email > ]
names
Array of strings

Responses

Request samples

Content type
application/json
{
  • "email": "user@example.com",
  • "name": "string",
  • "emails": [
    ],
  • "names": [
    ]
}

Response samples

Content type
application/json
{
  • "success": true,
  • "invited_count": 0,
  • "message": "string"
}

Webhooks

Webhook management

List webhooks

Returns all webhook subscriptions for your organization, including each webhook's subscribed events, enabled status, signing secret, and delivery success/failure counters. Check the counters to spot endpoints that are failing deliveries.

Authorizations:
OAuth2

Responses

Response samples

Content type
application/json
[
  • {
    }
]

Create webhook

Subscribes an HTTPS endpoint to real-time events. Deliveries are signed with HMAC-SHA256 in the X-Webhook-Signature header — verify it with the returned secret before trusting a payload (see the Developer Quickstart for example code). Failed deliveries are retried 3 times with exponential backoff.

After creating, use POST /api/v1/webhooks/{webhookId}/test to send a test event and confirm your endpoint receives and verifies it correctly.

Authorizations:
OAuth2
Request Body schema: application/json
required
url
required
string <uri>
events
required
Array of strings
Items Enum: "portal.created" "portal.workflow_changed" "file.uploaded" "file.signed" "task.created" "task.completed" "guest.invited"

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "webhook_id": "a47606a1-5b39-4a81-9480-c2cb738ff675",
  • "org_id": "a40f5d1f-d889-42e9-94ea-b9b33585fc6b",
  • "events": [
    ],
  • "secret": "string",
  • "enabled": true,
  • "success_count": 0,
  • "failure_count": 0,
  • "created_at": "2019-08-24T14:15:22Z"
}

Update webhook

Updates the URL, subscribed events, or enabled flag; at least one field is required. Setting enabled: false pauses deliveries without losing the configuration or signing secret — useful during maintenance windows on your receiving endpoint.

Authorizations:
OAuth2
path Parameters
webhookId
required
string <uuid>
Request Body schema: application/json
required
url
string <uri>
events
Array of strings
Items Enum: "portal.created" "portal.workflow_changed" "file.uploaded" "file.signed" "task.created" "task.completed" "guest.invited"
enabled
boolean

Responses

Request samples

Content type
application/json
{}

Response samples

Content type
application/json
{
  • "success": true,
  • "webhook_id": "a47606a1-5b39-4a81-9480-c2cb738ff675"
}

Delete webhook

Permanently removes a webhook subscription — deliveries stop immediately and the signing secret is invalidated. If you only need a temporary stop, disable it with the update endpoint instead.

Authorizations:
OAuth2
path Parameters
webhookId
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "success": true,
  • "webhook_id": "a47606a1-5b39-4a81-9480-c2cb738ff675"
}

Send test delivery

Sends a signed webhook.test event to the webhook's URL and reports the delivery result — use it to verify your endpoint is reachable and your signature verification works before relying on real events. The response's status_code is what your endpoint returned; a success: false with an error message usually means a network or TLS problem reaching your URL.

Authorizations:
OAuth2
path Parameters
webhookId
required
string <uuid>

Responses

Response samples

Content type
application/json
{
  • "success": true,
  • "status_code": 0,
  • "delivery_id": "9892f438-d31c-4ff2-bc84-146525b292ff",
  • "error": "string"
}