Client API documentation

Move your First AI Employee data with a contract you can reason about.

This hub documents the live, read-only Client API for approved software integrations. Registered clients can request tenant-scoped business records after the customer authorizes the exact OAuth scopes.

Base path
/api/client/v1

Live, versioned, and read-only.

Authentication
OAuth 2.0

Authorization Code with mandatory PKCE S256.

Data boundary
One tenant per grant

Tenant identity never comes from request input.

Start here

Client API overview

The Client API is a live, tenant-scoped, read-only interface for approved partners. V1 exposes calls, contacts, bookings, reviews, client accounts, jobs, tasks, pipeline opportunities, activities, saved views, reports, automation executions, dashboard summaries, and a complete field-allowlisted CSV catalog. Unregistered OAuth clients remain denied.

These docs do not grant access.

These routes require a registered OAuth client, an exact callback URI, and customer consent. Examples on this page are not credentials and cannot authorize a request.

Registered clients only

Unknown or disabled clients are denied. There is no public self-service client-registration endpoint.

Tenant from the grant

Every access token is bound to the customer that approved it. Callers cannot choose another tenant in a path, header, or request body.

Narrow scopes

Read and write permissions are separate. Reserved scopes do nothing until a real resource implementation ships.

Availability

Access and registration

Access starts with a reviewed integration and manual OAuth client registration. The public website never issues OAuth client credentials, API keys, or bearer tokens.

Live for approved integrations

Live with explicit registration

A registered client must use an exact HTTPS redirect URI and PKCE S256. The signed-in customer then approves the requested scopes for one business. Unknown or disabled clients cannot start an authorization flow.

What must happen before a first request

  1. First AI Employee reviews the integration and agrees on the minimum scopes.
  2. An administrator registers the client and its exact HTTPS redirect URIs. There is no self-service registration.
  3. The client creates a PKCE verifier and S256 challenge, then keeps the verifier until the callback.
  4. A signed-in customer approves the scopes on the First AI Employee consent screen.
  5. The partner exchanges the one-time code and stores the returned tokens securely.
Request developer access
Start here

Quickstart

Use this sequence after First AI Employee registers your integration. The example values are placeholders and will not work as credentials.

1. Register out of band

Provide the integration name, exact redirect URIs, contact, data use, retention plan, and requested scopes to First AI Employee. Client registration is manual.

2. Send the customer to consent

Build an Authorization Code request. Use state for request binding and PKCE with S256 for every Client API client.

3. Exchange the code once

POST form-encoded data to /oauth/token. Authorization codes are single use. Reuse is rejected.

4. Call a granted resource

Send the access token as a Bearer credential. Do not put tokens in URLs, logs, analytics, or browser storage.

Request
GET https://api.firstaiemployee.com/oauth/authorize
  ?response_type=code
  &client_id=client_example
  &redirect_uri=https%3A%2F%2Fpartner.example%2Foauth%2Fcallback
  &scope=calls%3Aread%20bookings%3Aread
  &state=opaque_csrf_value
  &code_challenge=base64url_sha256_verifier
  &code_challenge_method=S256
Request
curl "https://api.firstaiemployee.com/api/client/v1/calls?limit=50" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"

The Client API is read-only. Zapier has separate partner-specific write behavior that is not part of this contract.

Core concepts

OAuth 2.0

First AI Employee acts as an OAuth 2.0 authorization server. The implemented flow supports Authorization Code, exact registered redirect URIs, mandatory PKCE S256 for Client API clients, customer consent, hashed token storage, and refresh-token rotation.

Request
curl https://api.firstaiemployee.com/oauth/token \
  -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "client_id=client_example" \
  --data-urlencode "client_secret=stored_server_side" \
  --data-urlencode "code=one_time_authorization_code" \
  --data-urlencode "redirect_uri=https://partner.example/oauth/callback" \
  --data-urlencode "code_verifier=original_pkce_verifier"
Response
{
  "access_token": "returned_once_redacted",
  "refresh_token": "returned_once_redacted",
  "token_type": "bearer",
  "expires_in": 3600,
  "scope": "calls:read bookings:read"
}

Authorization flow

  • Authorization endpoint: GET /oauth/authorize.
  • Token endpoint: POST /oauth/token with application/x-www-form-urlencoded.
  • Resource endpoints accept OAuth Bearer access tokens only. Customer session cookies never authorize Client API requests.
  • Access token lifetime: 3,600 seconds.
  • Refresh token lifetime: 90 days. Every successful refresh returns a replacement refresh token.
  • The customer can disconnect the grant, which invalidates the integration.

Token response

Persist the new refresh token before discarding the old one. Presenting a rotated token again is treated as possible theft and can revoke the token family.

Client obligations

  • Keep confidential client secrets and refresh tokens in a server-side secrets store.
  • Validate state on the callback and send the original PKCE verifier during the code exchange.
  • Request only the scopes needed for the current feature.
  • On 401, refresh once and retry once. Do not loop. A 403 needs a new grant with the required scope.
Core concepts

Pagination and time

Every v1 list uses the same opaque cursor envelope. The cursor advances an offset into the current result order. It is not a database snapshot, so inserts during a long sync can shift later pages. Overlap and deduplicate during backfills.

Response
{
  "data": [
    {
      "id": "b50ff6fb-1f3a-4f4e-8462-07cbd6546f5f",
      "occurred_at": "2026-08-24T14:32:08.000Z",
      "caller": {
        "name": "Jordan Lee",
        "phone": "+15550100100"
      }
    }
  ],
  "meta": {
    "limit": 50,
    "next_cursor": "opaque_cursor_returned_by_server",
    "has_more": true
  }
}
  • Use limit between 1 and 100. The default is 50.
  • Send the returned next_cursor unchanged. Never parse or construct a cursor.
  • Deduplicate every resource by its stable id because records can move between offset pages while new data arrives.
  • For long-running syncs, persist the last successful cursor only after the destination transaction commits. Periodically restart from the first page and stop after reaching IDs already seen in a completed pass.

Dates, times, and phone numbers

Most timestamps are UTC ISO 8601 strings. Booking starts_at and ends_at are local wall-clock strings without an offset; interpret them with the current time_zone returned by /me. Phone numbers are returned in E.164 where source data permits. Preserve the original field when your destination has stricter formatting.

Core concepts

Errors and retries

OAuth endpoints use error and error_description. Implemented v1 resource errors add a stable request_id for support. Client API limiters use the smaller { error } body, so retry logic must key off the HTTP status and Retry-After. Normal authenticated budgets are isolated by the verified client and business pair. Do not branch on human-readable descriptions.

Response
{
  "error": {
    "code": "insufficient_scope",
    "message": "This token lacks the contacts:read scope.",
    "request_id": "req_01J..."
  }
}
HTTPMeaningClient action
400Malformed request, unsupported field, or invalid OAuth exchange.Fix the request. Do not retry unchanged.
401Missing, expired, revoked, or otherwise invalid access token.Refresh once, then reconnect if refresh fails.
403The token is valid but lacks the required scope.Ask the customer to authorize the missing scope.
404The resource is absent or not visible to this tenant.Treat as absent. Do not infer cross-tenant existence.
429Rate limit exceeded.Honor Retry-After and use exponential backoff with jitter.
Resources

Calls

Calls are tenant-owned call-history projections. The calls:read scope exposes an allowlisted record with caller details, timing, outcome, intent, sentiment, services mentioned, follow-up state, spam status, summary, and transcript.

Response
{
  "id": "b50ff6fb-1f3a-4f4e-8462-07cbd6546f5f",
  "occurred_at": "2026-08-24T14:32:08.000Z",
  "caller": {
    "name": "Jordan Lee",
    "phone": "+15550100100"
  },
  "duration_seconds": 164,
  "outcome": "follow_up",
  "intent": "estimate",
  "sentiment": "neutral",
  "services_mentioned": "water heater",
  "follow_up_needed": true,
  "spam_status": "not_spam",
  "summary": "Caller asked for a water heater estimate.",
  "transcript": "..."
}

Sensitive text

Call summaries and transcripts can contain personal or sensitive free text. Minimize what you copy, encrypt it at rest, restrict destination access, and apply a documented retention period.

Related read-only resources

GET /contacts

Contacts

Returns identity, notes, tags, classification, history, and recorded consent state. Requires contacts:read.

GET /reviews

Reviews

Returns platform, author, rating, text, analysis, and reply status. Requires reviews:read.

Resources

Bookings

The bookings:read scope returns booking projections with starts_at, ends_at, status, completed_at, and created_at. The official Client API has no booking write route in v1.

Request
curl "https://api.firstaiemployee.com/api/client/v1/bookings?limit=25" \
  -H "Authorization: Bearer ACCESS_TOKEN"

Treat status as source data and preserve the original value before mapping it to a destination enum. A booking record does not by itself prove that a specific third-party calendar still contains an event.

Events

Webhooks

Event delivery should complement polling, not replace reconciliation. Consumers must be able to process an event more than once and should periodically compare against the source API.

What exists now

The implemented hook manager is scoped to the Zapier adapter. It accepts only HTTPS hooks.zapier.com targets and supports lead.created and booking.created. It is not a public arbitrary-destination webhook API.

Reserved

Draft general contract

A future v1 webhook surface is reserved for registered HTTPS destinations, signed request bodies, per-event scopes, replay controls, and visible delivery history. No such general endpoint is open today.

Draft contract
{
  "id": "evt_01J...",
  "type": "lead.created",
  "created_at": "2026-08-24T14:32:09.000Z",
  "data": {
    "id": "lead_01J..."
  }
}
  • Acknowledge with a 2xx response only after the event is durably queued.
  • Use the event id as the idempotency key. Duplicate deliveries must be harmless.
  • Verify a signature over the raw request body before parsing JSON.
  • Reject stale timestamps and keep signing-secret rotation overlap short.
Data movement

Complete exports

The exports:read scope provides complete CSV portability across every field-allowlisted dataset already available to the account owner. The catalog spans calls, conversations, CRM, jobs, files, sales, tasks, reports, settings, custom fields, and their applicable history.

Implemented

Live with separate export approval

GET /exports returns the live catalog. GET /exports/{dataset}.csv streams a complete UTF-8 CSV. This broad surface is available only when the customer explicitly approves exports:read.

Request
curl "https://api.firstaiemployee.com/api/client/v1/exports" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"

curl "https://api.firstaiemployee.com/api/client/v1/exports/jobs.csv" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: text/csv" \
  --output first-ai-employee-jobs.csv

Safe import sequence

  1. Fetch the catalog and explicitly select reviewed dataset ids.
  2. Download each CSV to temporary storage and require a complete connection close.
  3. Validate headers, counts, JSON cells, time offsets, and required relationships.
  4. Import into a staging table before promoting destination changes atomically.
  5. Retry partial downloads from the beginning and delete staging files under the approved retention policy.
Data movement

Porting playbook

A reliable port is a checkpointed synchronization, not one large request. Start with the smallest useful resource set and record provenance in the destination.

1. Inventory

List required fields and decide whether each destination record is a snapshot, an event, or a mutable object.

2. Minimize

Request only the matching read scopes. Do not export recordings, free text, or phone numbers unless the destination actually needs them.

3. Backfill

Read the API's newest-first order in bounded pages. Commit one page atomically, then persist its cursor.

4. Catch up

Repeat from the checkpoint until lag is small. Overlap the final time window and deduplicate by source id.

5. Reconcile

Compare counts, time bounds, null handling, and a sample of source ids. Keep a failure queue for records that need manual mapping.

6. Operate

Use webhooks for low latency when supported, polling for repair, and an auditable disconnect and deletion procedure.

Recommended destination mapping

Source fieldDestination fieldRule
idsource_idStore verbatim and make unique with source_system.
occurred_atsource_occurred_atParse as UTC. Preserve precision.
contact.phonephone_e164Keep null distinct from an empty string.
summarysource_summaryTreat as sensitive free text. Do not use as a stable key.
statussource_statusStore the original value before mapping to local enums.
business_namesource_account_labelDisplay label only. Never use it as the tenant identifier.
Lifecycle

Versioning

The major version lives in the resource path. OAuth protocol endpoints remain unversioned. Successful resource responses also carry X-FAE-API-Version: v1. A new major version is required when a safe migration cannot preserve existing client behavior.

  • New optional response fields are compatible. Clients must ignore fields they do not understand.
  • New enum values are compatible. Store unknown values instead of rejecting the whole record.
  • Removing or renaming a field, changing its meaning, or making an optional field required is breaking.
  • Security fixes can change rejection behavior without a major version when preserving it would be unsafe.

Deprecation policy

V1 is live for approved integrations. A formal deprecation window will be documented before a breaking replacement. Draft and Reserved surfaces can still change before they become available.

Reference

Endpoint status

Status is part of the contract. Live means the official v1 route is available to registered clients with the required grant. Draft is a design example only. Reserved means no Client API resource implementation exists.

Official base path
https://api.firstaiemployee.com/api/client/v1
MethodPathScopeStatusNotes
GET/oauth/authorizerequested scopesImplementedBrowser consent. Registered clients only.
POST/oauth/tokennoneImplementedAuthorization code and rotating refresh grants.
GET/api/client/v1/meany valid grantLiveTenant-safe connection identity.
GET/api/client/v1any valid grantLiveRead-only service discovery.
GET/api/client/v1/openapi.jsonany valid grantLiveAuthenticated OpenAPI 3.1 contract.
GET/api/client/v1/callscalls:readLiveCall history and transcript projection.
GET/api/client/v1/contactscontacts:readLiveContacts and recorded consent state.
GET/api/client/v1/bookingsbookings:readLiveRead-only booking projection.
GET/api/client/v1/reviewsreviews:readLiveReviews and reply status.
GET/api/developer/v1/formsforms:readLiveForms a business owns and the responses they collected.
GET/api/client/v1/exportsexports:readLiveComplete field-allowlisted dataset catalog.
GET/api/client/v1/client-accountsclient_accounts:readLiveClient accounts with bounded contact and job summaries.
GET/api/client/v1/jobsjobs:readLiveTenant-scoped job records.
GET/api/client/v1/taskstasks:readLiveTenant-scoped Front Desk tasks.
GET/api/client/v1/pipeline-opportunitiespipeline_opportunities:readLivePipeline opportunity projections.
GET/api/client/v1/activitiesactivities:readLiveBounded CRM activity timeline.
GET/api/client/v1/saved-viewssaved_views:readLiveSaved-view definitions visible to the account.
GET/api/client/v1/saved-views/{id}/resultssaved_views:executeLiveExecute an authorized saved view.
GET/api/client/v1/report-definitionsreports:readLiveReport definitions and current summaries.
GET/api/client/v1/automation-executionsautomations:readLiveRecent automation execution projections.
GET/api/client/v1/dashboarddashboard:readLiveOwner-facing dashboard summary.
GET/api/client/v1/exports/{dataset}.csvexports:readLiveBounded-memory complete CSV stream.
GET/api/developer/v1/formsforms:readLiveForms, revisions and responses. Read only.
POST/api/client/v1/webhook-endpointsevent scopesReservedGeneral webhooks are not open.

Do not build production dependencies against a Reserved row. Request developer access to register an integration for the live v1 routes.

Forms has two documents of its own

The forms:read routes and the browser embed each have a full contract page: every endpoint with its page size, cursor and error envelope, and every embed mode with its attributes and events.