Workspace Settings and Integrations API

Customer-scoped settings, integration state, webhooks, activity, billing and agreements.

Authentication and scope

Send a workspace Organization API key as Authorization: Bearer your_api_key_here. Every response is scoped to the key's workspace. The API returns the same customer projection as the web app; staff-only fields and controls are unavailable. Admins can issue a key with explicit settings or API-key management permission in the web app. Ordinary keys have neither grant.

These endpoints are available at https://beam.lightmeter.io. Use JSON for request and response bodies unless the endpoint returns a PDF or an empty 204 response.

Workspace reads

MethodPathResult
GET/api/v1/settings/general/reply-forwarding-recipientsList configured addresses with their current mailbox scope. Also included in general settings as reply_forwarding_recipients.
POST/api/v1/settings/general/reply-forwarding-recipientsAdd {"email":"name@example.com"} without replacing other recipients. Up to 20 additional workspace-wide addresses can remain configured; existing workspace defaults and mailbox-specific destinations do not count toward that limit. Requires settings-management access and a fresh X-Operation-ID UUID for one verification email, subject to the existing five checks per workspace per hour.
DELETE/api/v1/settings/general/reply-forwarding-recipientsRemove the address supplied in {"email":"name@example.com"} from current and planned forwarding. Other addresses and forwarding mode stay unchanged. Repeating removal is safe.
GET/api/v1/settings/generalReply forwarding destination and mode, identity generation options, mailbox counts and capability flags.
GET/api/v1/team/membersMembers, roles and capabilities. Organization API keys cannot add or manage members.
GET/api/v1/activity/eventsWorkspace journal events. Supports limit 1–100, offset 0–1,000,000, and the web app's event, actor, source, campaign and date filters. API-key detail fields appear only for keys with the delegated API-key management grant.
GET/api/v1/settings/crm-integrationCustomer-visible HubSpot connection and sync state. Provider metadata refresh is reserved for workspace managers.
GET/api/v1/settings/crm-integration/closeCustomer-visible Close connection and sync state.
GET/api/v1/billing/overviewCustomer-visible balance, credits, invoices, reviews and billing settings. This is a read and creates no charge.
GET/api/v1/billing/platform-noticeCurrent customer-facing platform billing notice.
GET/api/v1/agreementsAgreements associated with the authenticated workspace.
GET/api/v1/agreements/{agreement_id}/pdfPDF for a workspace-owned agreement; inaccessible or unknown IDs return the same not-found response.
GET/api/v1/agreements/{agreement_id}/pdf/chunk?document=issued&index=0One Base64 chunk of a workspace-owned issued or executed PDF, at most 128 KiB before encoding. The response includes chunk count, total bytes and SHA-256 for assembly and integrity checks. Each read requires current authorization; no bearer document link or expiring cursor is issued, and repeating an index is safe. Unknown and inaccessible agreement IDs return the same not-found response.

Delegated settings and CRM management

The following writes require a key explicitly issued by a workspace Admin or Owner with can_manage_settings=true. Unprivileged keys receive 403. The browser uses the same handlers and retains its existing role checks, revision checks, validation and provider guards. The SES sending-account and staff account settings remain unavailable.

MethodPathAction
PATCH/api/v1/settings/generalChange verified reply forwarding destination. Requires X-Operation-ID UUID; while the budget store retains its keys, an ID is rejected for 24 hours and a workspace is limited to five verifications per hour. An uncertain result should be checked with GET before a new ID.
PATCH/api/v1/settings/general/reply-forwarding-modeChange forwarding mode after current worker/readiness checks.
PATCH/api/v1/settings/general/identity-generationChange identity-generation market, profiles and style.
POST/api/v1/settings/general/campaign-url-tracking/checkRequest a background sending-domain check for this workspace. Requires the settings-management grant. Repeated queued or running requests coalesce. Returns 202 with readiness; 503 means check status or scheduling is unavailable. A check changes no campaign policy and does not repair DNS or certificates.
PATCH/api/v1/settings/general/campaign-url-trackingSet the campaign creation default and explicit scope with {"enabled":false,"apply_to":"new_campaigns"} or apply_to="new_and_existing_campaigns", in either direction. Omitting scope preserves the legacy apply-to-existing behavior. Returns campaign_url_tracking and changed. Requires the settings-management grant.
PATCH/api/v1/settings/general/walkin-trackingChange walk-in tracking or the default source tag. Accepts a partial object with enabled, enable_utm_source booleans and/or an HTTP(S) default_redirect_url; at least one is required. Default destination changes atomically update only domains that follow the default. Returns walkin_tracking and changed. Requires the settings-management grant.
POST/api/v1/settings/general/identity-samplesPreview bounded generated identity examples; no saved setting change.
PATCH/api/v1/settings/domain-generationChange domain-generation industry keywords.

Request bodies: forwarding destination uses {"reply_forwarding_destination":"name@example.com","overwrite_acknowledged":true}; acknowledge replacement only after reviewing current mailbox destinations. Reply forwarding mode uses {"mode":"off"}, all_replies, or positive_replies. Identity generation and identity-sample preview use market, profile_weights (profile IDs and percentages totaling 100) and given_name_style from the general settings options. Domain generation uses {"industry_keywords":["example"]}, up to the current workspace limit. Read the current settings and allowed options before changing them.

Campaign click tracking

In Create or Edit → Creative, the campaign action menu offers Click tracking: On/Off. Each campaign inherits the workspace default when created and can then override it. New-only workspace changes preserve those choices; an explicit new-and-existing action resets matching existing campaign choices. PATCH /api/v1/campaigns/{campaign_id}/link-tracking accepts {"enabled":true} (or false). campaign_link_tracking_update exposes the same customer-scoped service through MCP. Campaign detail exposes url_tracking_enabled; the authenticated campaign API uses urlTrackingEnabled. Updates journal event108 with actor, source and before/after; prepared bytes and issued redirects are preserved.

In the app, use Settings → Tracking → Click tracking. This workspace setting starts off and currently covers campaign emails. The existing General API routes and MCP action remain unchanged. Actual changes appear in Activity with actor, source and Off/On before/after.

campaign_url_tracking.readiness reports state (first_check_pending, queued, checking, ready, failed or status_unavailable), nullable UTC next_check_at and last_checked_at, check_again_available, and this workspace’s domain outcomes with stable reason codes. Next check time comes from the live API scheduler and is a check time, not a readiness deadline. Lost or stale observations are reported as unavailable. PostgreSQL readiness remains the sending authority. Explicit enabling requests a prompt check through the same deduplicated background mechanism. MCP workspace_campaign_url_tracking_check with payload={} uses the same handler.

General settings returns campaign_url_tracking with enabled (the new-campaign default), scope_supported, existing_enabled_campaigns, existing_disabled_campaigns, managed_domains, ready_domains and unavailable_domains. Tracking defaults off, including missing settings. Campaigns inherit once at creation; clones are new, while retained drafts are existing. New-only scope never changes existing policies. The broader scope explicitly changes all retained non-deleted campaigns that differ, including completed and archived campaigns with attributable issued URLs. A default off while existing campaigns remain on is valid. Counts and settings changes serialize under the customer lock; the response includes the actual changed_campaigns count. A broad change works even if the default is unchanged; a true no-op returns changed=false. Invalid scope returns 400. MCP workspace_campaign_url_tracking_update uses the same payload and handler.

Readiness expires after ten minutes without renewed DNS, routing and HTTPS proof. Opted-in campaigns cannot launch, resume or make a fresh send when tracking is unavailable. Authorization, SMTP handoff and provider-attempt admission enforce this boundary; original-URL fallback is not allowed. Ordinary off sends remain available. Authorized MIME and message identity cannot be rewritten; previously untracked authorizations are held after opt-in until existing-campaign opt-out, while uncertain and accepted sends reconcile their original identity. Issued URLs always retain their saved destinations. Opting out an existing campaign stops future visits and Fingerprint submissions for its old URLs while preserving historical facts. Re-enabling is explicit and does not recover missed visits. Event108 records default and campaign changes, selected scope and actual counts transactionally. After an uncertain write response, read settings before retrying.

Eligible public HTTP and HTTPS links in recipient-rendered campaign messages become short HTTPS URLs on the naked sending domain. Interactive replies, test sends, attachments, protected authentication links and ambiguous prose candidates are excluded. A short URL publicly reveals its destination in the redirect response. Do not include a private bearer destination. Requests are classified as candidate estimates, automation, unknown or non-navigation evidence; neither missing detector data nor an ordinary browser proves a human. See click analytics for units and coverage.

Walk-in tracking

In the app, use Settings → Tracking → Walk-in tracking. These controls are independent of campaign click tracking and its new-campaign default. General settings also returns walkin_tracking: enabled, enable_utm_source, managed_domains, configured_domains, ready_domains and unavailable_domains. Tracking starts disabled. Pausing tracking keeps redirects and configured tags. The separate source preference controls utm_source=lightmeter on participating domains; existing opt-outs are preserved.

Readiness records recent reachability of a participating domain's root route. It expires after ten minutes and is separate from short-URL readiness; it does not gate an arriving redirect. Walk-in requests cannot identify a recipient or prove a human visit. Configure each domain and read coverage, estimates and unknown requests through the walk-in infrastructure API.

HubSpot and Close

Use /api/v1/settings/crm-integration for HubSpot and /api/v1/settings/crm-integration/close for Close, followed by one suffix below. API-key writes require the settings-management grant. Tokens in create, discover and rotate requests are sensitive; never log or persist them in client diagnostics. OAuth provider consent and callback remain in the browser flow. Status reads return saved state without provider discovery; use /discover for current metadata.

To disconnect an OAuth grant, send POST /api/v1/settings/crm-integration/oauth/disconnect for HubSpot or POST /api/v1/settings/crm-integration/close/oauth/disconnect for Close. Both require a JSON body with the current oauth_grant_revision from the settings read; Close also requires configuration_revision. HubSpot requires sync to be disabled first. A response may report revocation_pending; inspect connection state before retrying if the result is uncertain. API keys need the settings-management grant. OAuth start, pending inspection, completion, provider consent and callback remain bound to the initiating member's browser session.

MethodSuffix for either providerAction
GET(empty)Read connection and sync state.
POST(empty)Create the connection from a provider credential.
PUT(empty)Update sync settings with current configuration revision.
POST/discoverInspect provider account and capability metadata before setup.
POST/validateValidate the saved connection.
GET/test-conversation-previewPreview a positive-reply setup test without writing to the provider.
POST/test-conversation-operationsStart one bounded, durable positive-reply provider setup test.
GET/test-conversation-operations/{operation_id}Read a saved or uncertain provider test result without retrying it.
POST/conversations/{conversation_id}/retryRetry a workspace-owned conversation's pending sync.
POST/blocklist-previewReview provider blocklist changes before approval.
POST/blocklist-preview/approveApply the reviewed blocklist revision.
POST/activateActivate the configured integration after validation.
POST/capabilities/{capability}/activateEnable a supported sync capability.
POST/capabilities/{capability}/disableDisable a sync capability.
POST/rotate-tokenReplace the saved private credential.
POST/disableDisable the integration.

HubSpot credential requests use {"private_app_token":"..."}; Close uses {"api_key":"..."}. Create includes that credential plus a settings object. Update uses {"settings":{...}} for HubSpot and also requires configuration_revision for Close. Populate the settings object from the matching provider's current GET response and discovered option IDs; do not submit another provider's settings. HubSpot settings include lead pipeline and disqualified stage IDs, selected sync capabilities, source/lead-status mappings, phone block fields, and email subscription IDs. Close settings include selected sync capabilities, Lead status/source mapping, field assignments and optional Opportunity mapping. Preview/retry and blocklist approval use the IDs and revision returned by their corresponding preview/read response.

For Close, email_observation is a separate read capability. Activate it with POST /api/v1/settings/crm-integration/close/capabilities/email_observation/activate and {"configuration_revision":"CURRENT_GET_REVISION"}. An Admin or Owner, or an API key with the settings-management grant, may activate it after the saved account and permissions to read events and emails are checked. Both existing write capabilities keep their current opt-outs. Read integration.capabilities.email_observation.enabled, positive_reply_sync.enabled, and blocklist_sync.enabled from the Close GET response to verify the result. Disable observation with the matching /disable suffix; the original forward cutoff is retained on resumption. Close Notes and emails without native occurrence and exact conversation evidence do not qualify. Observation covers checked Close activity, not every mailbox email.

Close Contact reconciliation reads a current Lead snapshot once across the pending interval. These mutable snapshots cannot certify complete history. Lightmeter stops snapshot paging after the page at offset 10,000; omitted history remains a reported coverage gap. Close may impose a lower endpoint-specific offset limit. Native Event Log reads use cursor paging and have a 30-day retention limit; neither limit is a promise of complete mailbox coverage.

MCP uses the same public handlers: call workspace_crm_capability_activate with provider="close", resource_id="email_observation", and the current configuration_revision in payload. Use workspace_crm_close_get for readback and workspace_crm_capability_disable to pause observation.

For a positive-reply provider setup test, first read GET /test-conversation-preview?conversation_id=.... Then send POST /test-conversation-operations with a new client-generated UUID in operation_id, plus conversation_id, expected_trigger_message_id, and expected_recipient_email from the preview. For Close, also send the preview's expected_trigger_sender and acknowledge_sender_mismatch=true when sender and Contact email differ. Both providers use this suffix under their CRM base path. The request may write to the connected CRM. Reusing the same operation ID and body returns the saved outcome without another provider call. If the response is lost, read GET /test-conversation-operations/:operation_id before any new attempt. running or indeterminate means the provider outcome is not yet confirmed; another public test for that conversation is blocked until it is reconciled. failed_preflight means no provider sync began; use a new operation ID after fixing the input. At most ten new operations may be reserved per integration per hour; excess requests return 429. The provider attempt has a two-minute deadline, and a stale running record reads as indeterminate. An indeterminate result requires manual provider inspection; the API does not automatically replay it. API keys require the settings-management grant.

Revenue attribution setup is available at GET /api/v1/settings/crm-integration/revenue-setup and PUT /api/v1/settings/crm-integration/revenue-setup. PUT requires the settings-management grant. Its JSON body contains qualified_stage_ids, won_stage_ids, amount_meaning, optional monthly_target_cents, and Close's timezone and provider_value_period. Select stage IDs from the GET metadata. The provider-side /test-blocklist-round-trip route remains in the browser while durable cleanup and result lookup are defined.

Delegated API-key management

Only keys an Admin or Owner explicitly issued with can_manage_api_keys=true can call these endpoints. API callers cannot grant delegated settings or key-management privileges to newly created keys. An API caller cannot extend itself or another delegated key. Delete a compromised delegated key in the web app or through a different authorized manager.

MethodPathAction
GET/api/v1/api-keysList workspace keys and delegated capability flags, never token values.
POST/api/v1/api-keysCreate a normal key with display_name, expiration_option and optional expires_at. The token appears once; responses use Cache-Control: no-store.
PATCH/api/v1/api-keys/{apiKeyID}Rename or change expiration of a normal workspace key.
DELETE/api/v1/api-keys/{apiKeyID}Revoke a workspace key.

Webhook configurations

Use the multi-event configuration contract. The legacy scalar browser routes are not part of this public API. Creating, deleting, rotating or testing a webhook requires the key's explicit settings-management grant. See the web app webhook guide for supported events, delivery formats and payload examples.

MethodPathResult
GET/api/v1/webhook-configurationsWorkspace configurations; signing secrets are omitted.
POST/api/v1/webhook-configurationsCreate a configuration with HTTPS url, name, event_types and optional format. A signing secret is returned only at creation when the format uses signed JSON headers.
DELETE/api/v1/webhook-configurations/{id}Delete a workspace-owned configuration; returns 204.
POST/api/v1/webhook-configurations/{id}/signing-secret/rotateReplace a signed JSON secret. Update your receiver immediately; the new secret appears once in the response.
POST/api/v1/webhook-configurations/{id}/testSend sample events to the saved endpoint and record delivery results. Requires a new UUID in X-Operation-ID. While the budget store retains its keys, an ID is rejected for 24 hours and a workspace is limited to 20 test calls per hour.
POST/api/v1/webhook-configurations/test-previewTest an unsaved public HTTPS url with event_types and optional format. Private, loopback and link-local destinations and redirects are blocked. Requires settings-management permission and a fresh UUID in X-Operation-ID; it shares the saved-test 20 calls per workspace per hour and 24-hour replay limits. This sends sample events externally but creates no configuration or delivery-history row. After an uncertain response, check the receiver before trying another ID.
GET/api/v1/webhooks/deliveriesDelivery history with web app filters, sort, limit 1–100 and offset.
GET/api/v1/webhooks/deliveries/{id}Details for a workspace-owned delivery.

Create request

{"name":"Qualified replies","url":"https://example.com/lightmeter-webhook","event_types":["positive_reply"],"format":"standard"}

A test call can contact your configured endpoint and create a test delivery record. On a timeout or uncertain response, inspect delivery history before using a new operation ID. A reused ID returns 409; the hourly budget returns 429 with Retry-After. If the budget store is unavailable, tests fail closed with 503.