> For the complete documentation index, see [llms.txt](https://docs.amigo.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.amigo.ai/developer-guide/platform-api/conversations/surfaces.md).

# Surfaces

Surfaces are agent-generated data collection interfaces delivered to patients through supported communication channels. Agents analyze entity state and data gaps in the [world model](/developer-guide/platform-api/data-world-model.md), generate a `SurfaceSpec`, and the platform renders and delivers it. Submissions return as source-attributed world-model events; any later external delivery follows a separate connector policy.

{% hint style="info" %}
**Conceptual overview.** For the architecture, healthcare examples, and design rationale, see [Surfaces](https://docs.amigo.ai/channels/surfaces) in the conceptual docs.
{% endhint %}

## Voice Agent Surface Tools

A family of context graph tools allows voice agents to create and manage surfaces during live calls:

| Tool                           | Parameters                                                                                                                                      | Returns                                                                                                                                                           |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `create_surface`               | `entity_id` (required), `title` (required), `fields` (required, list), `description`, `channel`, `expires_in_hours`, `resource_type`, `context` | `surface_id`, `url`, `status`, `expires_at`                                                                                                                       |
| `create_surface_from_template` | `template_id` (required), `entity_id` (required)                                                                                                | `surface_id`, `url`, `status`, `prefilled_count`, `total_fields`. Resolves a form template against the patient's entity state, prefilling fields with known data. |
| `get_form_templates`           | optional `category`, `patient_type` filters                                                                                                     | Reusable form templates for the workspace                                                                                                                         |
| `deliver_surface`              | `surface_id` (required), `channel_address` (required: phone number or email)                                                                    | `surface_id`, `url`, `status`                                                                                                                                     |
| `check_surface_status`         | `surface_id` (required)                                                                                                                         | `surface_id`, `status`, `title`, `fields_count`, `entity_id`, `created_at`                                                                                        |
| `get_surface_progress`         | `surface_id` (required)                                                                                                                         | Field-level completion status (which fields are filled vs empty)                                                                                                  |
| `reshape_surface`              | `surface_id` (required)                                                                                                                         | New `surface_id` and `url` containing only the unfilled fields from an abandoned form                                                                             |
| `list_entity_surfaces`         | `entity_id` (required), `status` (optional filter)                                                                                              | `surfaces[]` (id, title, status, channel, created\_at), `count`                                                                                                   |
| `get_surface_insights`         | `entity_id` (required)                                                                                                                          | `entity_id`, `completion_rate`, `preferred_channel`, `total_surfaces`, `completed`, `pending_surfaces`, `recent_surfaces[]`                                       |
| `extract_surface_file_text`    | `surface_id` (required)                                                                                                                         | Readable text extracted from submitted PDF file fields                                                                                                            |

`get_surface_insights` queries the entity's surface analytics before creating new surfaces. Returns completion rate, preferred channel, and count of pending (unfinished) surfaces. Call this before `create_surface` to avoid surface fatigue: if a patient already has multiple pending surfaces or a low completion rate, the agent may choose to collect data verbally instead.

These tools proxy through the Platform API surface endpoints. Authentication uses the call's `agent_session` JWT, which is workspace-scoped, so surface operations are automatically isolated to the correct workspace.

All of these tools handle errors gracefully: if the Platform API is unreachable, the agent receives an error message rather than an exception, so it can continue the call.

## Permissions

Two scopes control surface access:

| Scope            | Grants                                                |
| ---------------- | ----------------------------------------------------- |
| `surfaces:read`  | View surface specs, submissions, and lifecycle status |
| `surfaces:write` | Generate new surface specs                            |

Both scopes are included in the `member` role by default. RBAC enforcement is applied on all surface CRUD routes (view, create, update, delete).

## Management API

### Create a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### List Surfaces

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Get a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/{surface\_id}" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Update a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/{surface\_id}" method="patch" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Archive a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/{surface\_id}" method="delete" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### List Surfaces Pending Review

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/review" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Approve a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/{surface\_id}/approve" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Reject a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/{surface\_id}/reject" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Reshape a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/{surface\_id}/reshape" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Get Surface Progress

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/{surface\_id}/progress" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Deliver a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/surfaces/{surface\_id}/deliver" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

The delivery endpoint sends the surface URL to the patient, marks the surface as `delivered`, writes an audit event, and publishes a `surface.delivered` SSE event for real-time dashboards.

**Delivery behavior**: email-shaped delivery targets are sent through the workspace's configured email transport (the surface must have been created with `channel='email'` and a bound `use_case_id`). The email body contains a short redirect link (`/s/f/{surface_id}`) that 302-redirects to the full signed token URL. Non-email, non-phone targets record a web handoff. Phone-number targets are rejected with `422`.

**Error responses**: the deliver endpoint returns `404` (surface not found), `409` (already delivered/completed/expired, or test surface), `422` (validation, including a rejected phone target), and `429` (rate limited). Email delivery additionally returns `503` when email transport is not configured.

**Lifecycle SSE events**: `surface.delivered`, `surface.opened`, `surface.submitted` are published in real time as they happen.

**Surface expiry**: Non-terminal surfaces (not yet completed) automatically expire after their configured TTL. Expired surfaces reject renders, submissions, and auto-saves.

## Channels

| Channel | Value   | Delivery Method                                                                  |
| ------- | ------- | -------------------------------------------------------------------------------- |
| Email   | `email` | Email with branded link                                                          |
| Web     | `web`   | Direct link for portals or dashboards                                            |
| SMS     | `sms`   | Spec value only - link delivery is not supported (returns 422); use email or web |

{% hint style="info" %}
While `sms` is a valid channel value on a spec, the deliver endpoint does not support SMS link delivery (phone-shaped addresses return `422`). Delivery is email or web.
{% endhint %}

## BrandingConfig

Visual branding applied to the patient-facing form. Can be set at workspace level (default for all surfaces) or per-surface (overrides workspace defaults).

### Get Workspace Branding

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/settings/branding" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Update Workspace Branding

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/settings/branding" method="put" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

When rendering a surface, the platform merges workspace branding with surface-level branding. Surface-level values take precedence. Changes to workspace branding are audit-logged.

## Lifecycle

```mermaid
%%{init: {"theme": "base", "themeVariables": {"primaryColor": "#D4E2E7", "primaryTextColor": "#100F0F", "primaryBorderColor": "#083241", "lineColor": "#575452", "textColor": "#100F0F", "clusterBkg": "#F1EAE7", "clusterBorder": "#D7D2D0"}}}%%
stateDiagram-v2
    [*] --> Created : Agent generates SurfaceSpec
    Created --> PendingReview : Awaiting field-engineer review
    Created --> Delivered : Sent via channel (email, web)
    PendingReview --> Delivered : Review approved
    Delivered --> Opened : Patient opens link
    Opened --> Partial : Auto-save (some fields)
    Partial --> Partial : Additional fields saved
    Opened --> Completed : All required fields submitted
    Partial --> Completed : Remaining fields submitted
    Created --> Expired : TTL exceeded
    Delivered --> Expired : TTL exceeded
    Opened --> Expired : TTL exceeded
    Partial --> Expired : TTL exceeded
    Created --> Archived : DELETE/archive route
    Delivered --> Archived : DELETE/archive route
    Completed --> Archived : DELETE/archive route
    Completed --> [*]
    Expired --> [*]
    Archived --> [*]
```

| Status         | Value            | Description                                            |
| -------------- | ---------------- | ------------------------------------------------------ |
| Created        | `created`        | Spec generated and stored                              |
| Pending review | `pending_review` | Surface awaiting field-engineer review before delivery |
| Delivered      | `delivered`      | Sent to patient via channel                            |
| Opened         | `opened`         | Patient opened the link                                |
| Partial        | `partial`        | Some fields auto-saved                                 |
| Completed      | `completed`      | All required fields submitted                          |
| Expired        | `expired`        | TTL exceeded without completion                        |
| Archived       | `archived`       | Soft-deleted via the DELETE/archive route              |

## Data Flow

Submissions are written to the world model as events:

* **Source**: `surface` (distinguishable from EHR, voice, or manual imports)
* **Confidence**: Initial confidence level for patient-reported data
* **Entity**: Emitted lifecycle events carry the surface's `entity_id`, allowing supported world-model projections and timelines to associate them with that entity

When a patient submits a surface, the platform first commits the surface row as completed. It then schedules `surface.submitted` world-event emission asynchronously on a best-effort path. Timeline and entity projection updates therefore happen separately and can lag the successful submission response; an emission failure is logged rather than rolling back the committed surface.

An emitted surface event retains source provenance and confidence. Any later entity resolution or typed projection depends on the downstream pipeline for that event shape rather than a universal synchronous rule. Where the private-preview connector review flow is enabled, a configured downstream mutation can stage a separate external write proposal; reviewing that proposal does not change the surface event's confidence.

## Patient-Facing Endpoints

Patients access surfaces through HMAC-signed URL tokens. No login or account is required, and each token grants access to one specific surface.

### Open a Short Surface Link

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/s/f/{surface\_id}" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Render a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/s/{token}" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Submit a Surface

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/s/{token}/submit" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Save a Surface Field

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/s/{token}/fields/{key}" method="put" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

The short-link operation issues a redirect to a fresh signed-token URL and reports missing or expired surfaces separately. Rendering marks the surface as opened. Submission commits the completed surface before scheduling best-effort world-event publication. Field saves update the lifecycle to partial.

**Token format**: HMAC-SHA256 signed with a configured secret. Contains surface ID and expiration. Validated on every request.

**Rate limiting** (per IP):

* Render: 30 requests/minute
* Submit: 10 requests/minute
* Auto-save: 30 requests/minute

**Field value validation**: Individual field values are capped at 10KB to prevent payload abuse.

**Rendering**: server-side HTML generation for all 14 field types. Mobile-first, XSS-safe. Photo and file uploads use native device capabilities. Signature fields use touch input.

**Lifecycle concurrency**: each status update is committed against the surface row. Submission locks the row while checking terminal state and marking it completed, which rejects a competing repeat submission. World-event emission is outside that transaction.

When creating a surface via the management API, the response includes the signed `token` and full `url` if the token secret is configured.

## Gap Scanner Settings

The gap scanner is a background loop on the connector runner that proactively detects missing entity data and creates surfaces. Configuration is per-workspace via settings endpoints.

### Get Gap Scanner Settings

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/settings/gap-scanner" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Update Gap Scanner Settings

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/settings/gap-scanner" method="put" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Preview Gap Detection

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/settings/gap-scanner/preview" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Run a Gap Scan

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/settings/gap-scanner/scan" method="post" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

Updates require an admin or owner and support partial bodies. Preview performs a dry run without creating surfaces. Scan triggers one scan tick on demand.

The scanner reports through the connector runner's metrics. Changes to settings are audit-logged.

## Surface Analytics

These workspace-scoped operations provide closed-loop optimization data and require viewer-or-higher permissions.

### Get Surface Completion Rates

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/analytics/surfaces/completion-rates" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Get Surface Channel Effectiveness

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/analytics/surfaces/channel-effectiveness" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Get Surface Field Abandonment

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/analytics/surfaces/field-abandonment" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

### Get an Entity's Surface History

{% openapi src="<https://api.platform.amigo.ai/v1/openapi.json>" path="/v1/{workspace\_id}/analytics/surfaces/entity/{entity\_id}" method="get" %}
<https://api.platform.amigo.ai/v1/openapi.json>
{% endopenapi %}

## Entity Type

Surfaces introduce the `interaction` entity type to the world model ontology. Surface lifecycle events (created, delivered, opened, submitted) are tracked as first-class entities with full event sourcing.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.amigo.ai/developer-guide/platform-api/conversations/surfaces.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
