Infrastructure API

Use an organization API key in Authorization: Bearer <key>. Every result is scoped to that key's workspace. These read endpoints use the same inventory and date-range projections as the customer Infrastructure views.

EndpointResult
GET /api/v1/infrastructure/domainsDomain inventory, assignment and use counts, capacity, daily sends, and available domain health.
GET /api/v1/infrastructure/mailboxesMailbox inventory, lifecycle and warming state, capacity, assignment, and recent activity. Each item's ramp_up gives its stage (first_week, stepping_up, at_full_limit, held, reduced or fixed_limit), hold reason, full limit and progress; ramp_up_stage_counts counts the listed mailboxes by stage when any is automatic. Maintenance credentials are never included.
GET /api/v1/infrastructure/distributionCampaign mailbox assignments and recently used distribution.
GET /api/v1/infrastructure/capacityWorkspace capacity as shown in Infra › Capacity: daily_capacity today, subscribed_per_day (null when not recorded), sent_per_day and unused_per_day over the last 7 complete UTC days with unused_reason, loaded_leads, new_leads_per_sending_day and lead_runway (state, sending_days, lasts_until), ramp_up stage counts for sending mailboxes when any is automatic, warm_up for a new workspace that has never sent campaign email (day of days, complete, ends_on (the first day after warm-up), sending_starts_on, mailbox_count (projected senders), backup_mailbox_count, floor_daily_limit, ceiling_daily_limit, daily_capacity_from_sending_start, reaches_subscribed_on, ramp_up_finishes_on or still_growing_after; unused_reason is then warm_up), and days: 14 history days and today plus horizon_days (21 default, or 42) with daily_capacity, sent or estimated scheduled. Projected capacity follows the ramp-up projection; during warm-up it is 0 until sending_starts_on and then assumes every prepared automatic mailbox sends on weekdays. Scheduled sends are an estimate from each campaign's last 7 days. Dates are UTC days.
GET /api/v1/infrastructure/capacity-allocationCapacity allocation as shown in Infra › Allocation. Workspace totals: in_campaigns_per_day, usable_per_day (in campaigns plus Available), available_per_day, reserve_per_day and reserve_target_per_day (equal to subscribed_per_day; null without a subscription), labelled_backup_per_day, recovering_per_day, warm_up_per_day and delivered (last full Monday–Friday week: week_of, sent_per_day, sending_days, percent_of_subscribed). campaigns lists draft, submitted, active, ending and paused campaigns that hold domains or have leads, with capacity_per_day, domain_count, sent_per_day_last_5_days, leads (total, not_yet, started, completed), completes_on from the campaign send timeline, plannable (overlap on and provider routing set up) and shared_domains. days runs from history_from (optional query parameter, YYYY-MM-DD; default 14 days back, at most 180) through the projection, with each campaign's capacity and sends, earlier campaigns, and Available, Reserve, Recovering and warm-up capacity. History capacity starts with daily capacity history; earlier days carry sends only. needs_attention lists suggestions; nothing moves automatically. can_apply_plans is always false and labelling_dry_run (the staff-only automatic labeling counts) is always null for an API key. Dates are UTC days.
POST /api/v1/infrastructure/capacity-plans/previewPreview a capacity plan without changing anything. Body: campaigns (up to 20: campaign_id, unit pct of usable capacity, sends_per_day or domains to add (a whole number up to 500), value, use_reserve) and returns (up to 20 completed, ended or paused campaigns, at either overlap setting: campaign_id, started_leads keep_on_current or reassign, optional domain_count), limited to 64 KiB. The result lists the whole domains each campaign would gain, from Available or the reserve, with per-target capacity, before_per_day counted as Allocation counts it (a shared domain once, under the campaign that is sending), completion before and after, and shortfall_reason/shortfall_text when it falls short; returns list the domains given back. A domain shared by two campaigns in one plan can be returned only once. A return is refused while the campaign still has assignments on inactive mailboxes, and reassign is refused when no domain the campaign keeps is in every sending pool the started leads use; any refused return makes the whole plan not applicable. A plan never takes domains from a running campaign, never splits a domain and never creates sharing. Only campaigns with overlap on and provider routing set up can gain domains. applicable is always false for an API key: Lightmeter staff apply plans in the UI or through MCP.
GET /api/v1/infrastructure/campaign-assignmentsBounded active Campaign mailbox assignments with campaign, mailbox, and domain references. domain_campaign_visible distinguishes campaigns included in the domain list from historical campaigns still present in mailbox and distribution lists. Defaults to 100 items; use limit from 1 to 100 and page.next_cursor to continue.
GET /api/v1/infrastructure/onboarding-build-statusAggregate domain and mailbox build progress for the workspace. Returns 204 when there is no current onboarding build.
GET /api/v1/infrastructure/ramp-up/domainsCustomer-visible domain and mailbox health, ramp-up progress, and allocation policy evidence. Optional include_health=false and include_mailboxes=false reduce work and response size.
GET /api/v1/infrastructure/ramp-up/domains/{domain_uuid}/mailboxesBounded ramp-up mailbox rows for one active workspace domain. Defaults to 100 rows; use limit (1 to 100) and page.next_cursor. Set compact=true to omit large campaign arrays and use campaign-assignment pages for those relationships.
GET /api/v1/infrastructure/ramp-up/domains/{domain_uuid}/mail-server-sent-countsMail-server sent counts for one workspace domain and date range.
GET /api/v1/infrastructure/ramp-up/mailboxes/{mailbox_id}Reputation, volume, projection, and ramp-up history for one workspace mailbox. ramp_up adds its stage, effective, next-step and full limits, earned days, last and next check, and the latest check results (checks: Infra Health against the floor, campaign email sent, assignment to a running campaign, Content Health, able to send; null means not checked).
GET /api/v1/infrastructure/ramp-up/mailboxes/{mailbox_id}/send-logCursor-paged campaign send log for one workspace mailbox. Warming and other staff send types are unavailable.
POST /api/v1/infrastructure/domains/exportStream customer-visible domains as CSV. Optional JSON body: {"domain_uuids":["..."]}, limited to 64 KiB. Omit the selection to export all customer-visible domains.
POST /api/v1/infrastructure/mailboxes/exportStream customer-visible mailboxes as CSV. Optional JSON body: {"mailbox_uuids":["..."]}, limited to 64 KiB. Omit the selection to export all customer-visible mailboxes. Login credentials cannot be exported through an organization API key.

Date range

Omit query parameters for the rolling 5d window, or set window=24h. For a custom inclusive UTC date range, provide both start_date and end_date in YYYY-MM-DD format. Custom ranges are limited to 90 days. Invalid ranges return 400.

Example: GET /api/v1/infrastructure/domains?start_date=2026-09-01&end_date=2026-09-29.

The response retains the browser's window, date_range, summary, and items fields. Domain responses also include current capacity and daily-send fields. These are workspace inventory views and do not include staff provisioning or login credentials.

Bounded retrieval

Domain, mailbox, distribution, ramp-up domain, and domain mail-server sent-count lists accept limit from 1 to 100. Pass the returned page.next_cursor as cursor, with the same limit, workspace, date controls, and ramp-up options, until no next cursor remains. An omitted limit preserves the existing full response. Opaque cursors are limited to 1024 characters to bound untrusted input; return the server-issued value unchanged. Invalid cursors, or cursors used with another workspace or different filters, return 400; an inventory or projected result that changes during traversal returns 409 and requires a restart from the first page.

Domain and mailbox pages return tenant-scoped items. Their page.summary_scope=page means summary covers only returned items; domain daily-send and capacity metadata remain workspace-wide. Set compact=true on domain or mailbox lists to omit potentially large nested campaign arrays, then traverse /infrastructure/campaign-assignments for those relationships. Domain campaign references include only assignments with domain_campaign_visible=true; mailbox references include the other returned historical assignments too. Set compact=true on distribution to retain its counts while omitting nested domain and mailbox arrays; the same assignment pages carry the members. Distribution, ramp-up, and sent-count pages retain workspace-wide summary fields and report page.summary_scope=workspace. Pagination bounds the returned data for grouped results, but processing time may still grow with workspace size.

Cursors are tied to the first page's time, active workspace, filters, and compact mode. Inventory membership changes trigger a restart; grouped projections also restart when their contents change. Results are not a frozen database snapshot across requests. For a ramp-up domain whose nested mailbox rows exceed 128 KiB even at limit=1, request include_mailboxes=false, then traverse /infrastructure/ramp-up/domains/{domain_uuid}/mailboxes with compact=true as needed. The assignment pages restore omitted campaign references. Individual mailbox reputation, volume, projection, and capped 100-entry history remain available at the separate mailbox-detail endpoint.

CSV exports accept the same date controls. Selected UUIDs are deduplicated; an empty selection on the public API exports all visible rows. Values that could be interpreted as spreadsheet formulas are neutralized in the CSV. HTTP downloads are staged before delivery and limited to 128 MiB. OAuth MCP export tools require 1 to 500 selected UUIDs and return CSV text up to 128 KiB. OAuth-only clients can traverse inventory pages and export selected UUIDs in batches. If one row's campaign names exceed the MCP limit, use compact=true on the export: the campaigns cell is blank while assignment status is preserved; campaign names and relationships remain available through campaign-assignment pages. A single all-inventory CSV export requires the public API and an organization API key.

Mailbox send log filters

The send log accepts the date controls above, or a pair of RFC3339 start_at and end_at timestamps with at most microsecond precision and a range of at most 90 days. Do not combine timestamp and date/window controls. Optional filters are time_zone (IANA name, default UTC), recipient_classification (G, M, S, O, or unknown), campaign_id, and order (asc or desc). Use limit from 1 to 100 and the returned next_cursor for the next page with unchanged filters. A changed snapshot returns 409 and should be restarted from the first page.

Walk-in destinations and reporting

A walk-in request visits the root of a sending domain, such as https://sending.example/. It redirects to the workspace's configured website. It identifies neither an email recipient nor the campaign that caused the visit. Personalized URL clicks have separate metrics.

EndpointContract
GET /api/v1/infrastructure/domains/{domain_uuid}/walkinRead one owned domain's destination, participation and UTM campaign label, including its effective destination and readiness.
PATCH /api/v1/infrastructure/domains/{domain_uuid}/walkinUpdate any of redirect_url, enabled and utm_campaign. Supply at least one field. Requires the settings-management grant; the web app and MCP require a workspace Admin, Owner or authorized staff member.
PATCH /api/v1/infrastructure/domains/walkinAtomically enable or disable 1–200 active owned domains with {"domain_uuids":["uuid"],"enabled":true}. Requires the same settings-management permission. Duplicate UUIDs are applied once; any invalid, inactive or inaccessible domain rejects the entire selection. Returns {"domains":[{"domain_uuid":"uuid","enabled":true,"changed":true}]} only after commit. Preserves destinations and analytics labels; disabling remains available while workspace tracking is paused. Domain participation affects all mailboxes on each domain, and does not imply routing readiness.
GET /api/v1/reports/walkinRead aggregate observed requests and classification coverage for the workspace. Accepts the date controls above, optional domain_uuid and include_retired=true.

The domain response contains domain_uuid, domain, redirect_url, enabled, utm_campaign, effective_redirect_url, ready, can_update, workspace_tracking_enabled, enable_utm_source, default_redirect_url and use_default. Updates also return changed; saving unchanged values is safe. A missing or inaccessible domain returns the same 404 response. An invalid effective configuration returns 400. Enabling domain tracking while workspace tracking is disabled returns 409. A destination must be an absolute HTTP or HTTPS URL without embedded credentials, control characters or a redirect back to the sending domain. An unconfigured or invalid saved destination has a null preview.

Example request body: {"redirect_url":"https://www.example.com/landing","enabled":true,"utm_campaign":"Autumn launch"}. The UTM campaign label is text for the destination website's existing analytics; it is not a campaign ID. Domain participation controls walk-in tagging and tracking eligibility. Workspace tracking is a separate, initially disabled setting at PATCH /api/v1/settings/general/walkin-tracking. The domain response includes that workspace state and source preference. Set use_default:true to follow the current workspace destination; use_default:false freezes the current URL as custom. Supplying redirect_url alone selects Custom. A conflicting URL with use_default:true is rejected.

Disabling workspace tracking keeps redirects working. Configured destinations and UTM choices take priority over visitor query parameters. Destination changes do not rewrite destinations already issued in personalized URLs. Root navigation uses an ordinary temporary HTTP redirect without a JavaScript interstitial; asynchronous classification never waits on the destination response. Navigation depends on application/database availability.

Report values and limits

The report contains date_range, tracking_status, summary, domains, series and campaign_associations. Each count group contains high_confidence_navigation_count (retained navigation estimate), optimistic_navigation_count (inclusive estimate plus positively evidenced uncertain visits), observed_requests, estimated_navigation_count, automated_requests, unknown_requests and non_navigation_requests. These count requests, not unique people. Normal reports show visits with a grey bracketed optimistic total only when it is larger. Existing diagnostic fields and nullable estimate behavior remain compatible. HEAD and prefetch requests do not count as navigations.

estimated_navigation_count is a bot-filtered navigation estimate, not verified humans. It is null when unknown observations exist and no qualifying estimate is available. Pending, unavailable and unsupported classification remains unknown; do not convert null to zero. A successful empty report means no recorded data in that range. An API failure must remain an error.

tracking_status distinguishes paused, unavailable, active and incomplete. Readiness describes routing and tracking prerequisites, not detector accuracy. Default domain performance rows include active participating domains with zero recorded requests and current domains with historical requests. Domains no longer active in this workspace require the retired-domain view. The explicit retired-domain view can include facts captured for this workspace after the new tracking started. Reassignment never transfers another workspace's history. Historical data from the retired collector is not imported.

Campaign associations use current_campaign_at_visit only where one current campaign owned the domain at observation; otherwise their basis is unassigned. A late visitor may have seen an earlier campaign, so the association does not establish causation or recipient identity. No human-recognition accuracy, attribution percentage or deliverability guarantee is implied.

MCP exposes these same contracts through workspace_infrastructure_domain_walkin_get, workspace_infrastructure_domain_walkin_update workspace_infrastructure_domains_walkin_update and workspace_walkin_report_get. The bulk tool accepts domain_uuids and enabled directly. Domain tools accept domain_uuid; updates put the public API body in payload. Report arguments use the same date, domain and retired-domain filters. Workspace tracking/source-tag updates use workspace_walkin_tracking_update. All resolve an accessible workspace and retain the same permissions.

MCP

OAuth clients can use workspace_infrastructure_domains_list, workspace_infrastructure_mailboxes_list, workspace_infrastructure_distribution_list, workspace_infrastructure_campaign_assignments_list, workspace_infrastructure_onboarding_build_status_get, workspace_infrastructure_ramp_up_domains_list, workspace_infrastructure_ramp_up_domain_mailboxes_list, workspace_infrastructure_domain_mail_server_sent_counts_get, workspace_infrastructure_ramp_up_mailbox_get, workspace_infrastructure_mailbox_send_log_list, workspace_infrastructure_domains_export, and workspace_infrastructure_mailboxes_export. They accept the same applicable filters and resolve an accessible workspace_id before running the same public service contract. Every infrastructure MCP structured result, including domain mail-server sent counts, has a complete 128 KiB limit. For larger lists, start with limit=50, follow next_cursor, and reduce the limit or use compact=true plus assignment pages if one item is too large. A paged send log has its separate snapshot cursor contract. Export selected inventory UUIDs in bounded MCP batches; a single all-inventory CSV export still requires the public API and an organization API key. Lightmeter staff can switch mailboxes between Automatic and Manual ramp-up management with workspace_infrastructure_ramp_up_automation_set, which has no public API equivalent because API keys are never staff. capacity_allocation_get and capacity_plan_preview read and preview capacity allocation like the API above. Lightmeter staff apply a reviewed plan with capacity_plan_apply (its fingerprint must match the preview, or nothing changes), which has no public API equivalent. Capacity plans are available in every workspace; there is no per-workspace switch. Automatic labeling of mailboxes not in a campaign stays a dry run: Lightmeter staff list its suggested changes with capacity_labelling_review_get (Available to Recovering when unhealthy for every target for 2 checks running, Recovering to Available when healthy for Google or Microsoft for 2 checks running, and whole domains between Available and Backup to keep backups at the Subscribed target) and apply all or some of them by id with capacity_labelling_apply. Applying recomputes the suggestions, changes only the selected ones that are still suggested, reports the others as skipped (no_longer_suggested, or mailbox_status_refused when a mailbox's status changed while applying), and records each mailbox's new label in Activity. These two tools are staff-only and have no public API equivalent.