# Actions

Source: https://preview.ceyo.ai/docs/signal/actions

## Project scope

### Actions

Retrieve prioritized recommendations, supporting findings, implementation guides, status, and measured outcomes.

**Scope:** Project scope — `/projects/{project_id}/…`

### List actions

`GET /projects/{project_id}/actions`

Returns actions for the selected project, with server-side filtering and pagination.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Query parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `q` | string | Optional | Search titles, descriptions, recommendations, and targets. |
| `status` | string | Optional; Default: active | active, todo, in\_progress, completed, dismissed, or resolved. active includes todo and in\_progress. |
| `priority` | string | Optional | low, medium, high, or critical. |
| `action_type` | string | Optional | Restrict results to one action type. |
| `effort_level` | low \| medium \| high | Optional | Restrict results by estimated effort. |
| `source_category` | string | Optional | Restrict results by work area. |
| `topic_id` | uuid | Optional | Return actions linked to one topic. |
| `page` | integer | Optional; Default: 1 | The 1-based page number. |
| `per_page` | integer | Optional; Default: 25 | Number of actions per page. Maximum: 100. |

#### Response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `actions` | Action\[\] |  | Actions matching the selected filters. |
| `pagination` | Pagination |  | Pagination metadata. |

#### Action

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique action identifier. |
| `status` | string |  | todo, in\_progress, completed, dismissed, or resolved. |
| `priority` | string |  | low, medium, high, or critical. |
| `expected_outcome` | object |  | How the impact was estimated. \`state\` is critical\_rule, modelled, directional, or unknown. \`score\` (0-100), \`range\`, and \`unit\` are only present when state is modelled; \`assumption\` explains why. Display "unknown" rather than a number for unknown and directional outcomes. |
| `estimated_impact` | integer |  | Deprecated: relative ranking score from 0 to 100, not a forecast. Show expected\_outcome to customers; this value is present even when no outcome can be modelled. |
| `effort_level` | low \| medium \| high |  | Estimated implementation effort. |
| `feasibility` | low \| medium \| high |  | Estimated ability to complete the recommendation. |
| `source_category` | string |  | owned, earned, reputation, local, competitive, operational, or general. |
| `content_format` | string \| null |  | Recommended deliverable format when relevant. |
| `action_type` | string |  | technical, content, visibility, competitor, listing, earned\_editorial, earned\_ugc, earned\_reference, sentiment\_correction, general, access, measurement\_fix, entity, facts\_page, claim\_parity, content\_extend, local\_template, reputation\_programme, marketplace\_listing, directory\_listing, list\_inclusion, partner\_integration, community, or site\_health. |
| `title` | string |  | Short action title. |
| `description` | string \| null |  | Problem or opportunity addressed by the action. |
| `recommendation` | string \| null |  | Recommended outcome or approach. |
| `target_kind` | string |  | Type of resource targeted by the action. |
| `target` | string \| null |  | Target URL, prompt reference, profile, or scope value. |
| `structured_target` | StructuredTarget \| null |  | Execution-ready target label, platform, URL, and state. |
| `dependencies` | ActionDependency\[\] |  | Actions that should be completed first. |
| `external_profile` | ExternalProfile \| null |  | URL and availability status for an external profile. |
| `diagnosis` | object \| null |  | Structured diagnosis behind the selected action when available. |
| `expected_lift` | object \| null |  | Directional expected-lift estimate and assumptions when available. |
| `validation` | Validation \| null |  | Metrics, success conditions, timing, and comparison used to validate the action. |
| `validation_result` | ValidationResult \| null |  | Result after validation has observed the completed action. |
| `evidence_summary` | object \| null |  | Bounded response, citation, and source evidence for the action. Each top\_sources row may include supports: up to 3 competitors that source backs in AI answers. |
| `fixes` | Fix\[\] \| null |  | Machine-readable corrections when the action comes from AI answers stating a business fact wrong: the stated value, the correct value, its source, and the third-party pages found carrying the wrong value. Null for every other action. |
| `guide` | Guide \| null |  | Structured implementation guide when available. |
| `topics` | TopicReference\[\] |  | Topics connected through supporting prompts and findings. |
| `started_at` | datetime \| null |  | When the action first entered in\_progress. |
| `completed_at` | datetime \| null |  | When the action was completed. |
| `measured_outcome` | MeasuredOutcome \| null |  | Targeted-prompt presence before and after completion. Present only when status is completed. Pending until the next visibility run can be compared. |
| `dismissed_at` | datetime \| null |  | When the action was dismissed. |
| `resolved_at` | datetime \| null |  | When supporting findings were automatically resolved. |
| `created_at` | datetime |  | Action creation time. |
| `updated_at` | datetime |  | Last action update time. |

#### StructuredTarget

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `label` | string \| null |  | Target display label. |
| `platform` | string \| null |  | Target platform. |
| `url` | URL \| null |  | Target URL. |
| `state` | existing\_page \| proposed\_page \| external\_destination \| scope \| null |  | How the target should be handled. |

#### ActionDependency

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `action_id` | uuid |  | Required action identifier. |
| `title` | string |  | Required action title. |
| `reason` | string \| null |  | Why it should be completed first. |

#### ExternalProfile

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | found \| readable \| unavailable \| not\_found \| unknown |  | Availability of the external profile. |
| `url` | URL \| null |  | External-profile URL. |

#### Validation

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `observation_window_days` | integer \| null |  | Days allowed to observe the expected result. |
| `comparison` | string \| null |  | Plain-language description of how the result is evaluated. |

#### ValidationResult

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | success \| failure \| inconclusive \| not\_observable |  | Outcome of the latest validation observation. |
| `reason` | string \| null |  | Explanation when validation did not succeed. |
| `observed_at` | datetime \| null |  | When the result was last observed. |
| `window_ends_at` | datetime \| null |  | When the observation window ends. |

#### MeasuredOutcome

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | string |  | pending or measured. |
| `reason` | string \| null |  | Present when pending: awaiting\_post\_completion\_run, no\_baseline\_run, no\_targeted\_prompts, or missing\_targeted\_executions. |
| `outcome` | string \| null |  | Present when measured: improved, unchanged, or declined. |
| `targeted` | object \| null |  | Presence on this action’s validation prompts: before, after, and sample\_size. |
| `other` | object \| null |  | Presence on all other prompts in the same runs: before, after, and sample\_size. |
| `baseline_at` | datetime \| null |  | When the pre-completion visibility run finished. |
| `comparison_at` | datetime \| null |  | When the post-completion visibility run finished. |

#### Fix

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `field` | phone \| website \| address \| hours \| closed |  | The business fact AI answers get wrong. |
| `qualifier` | string \| null |  | Weekday or weekday range for hours (monday … sunday, monday-saturday). Null for other fields. |
| `wrong` | string |  | The value AI answers state, normalized: HH:MM-HH:MM or closed for hours; E.164 for phone; permanently, temporarily, or moved for closed. |
| `correct` | string \| null |  | The value on record, normalized the same way. |
| `correct_source` | website \| location\_record \| google\_business\_profile \| customer \| null |  | Where the correct value comes from. |
| `stated_by` | string\[\] |  | AI models whose answers state the wrong value. |
| `carried_by` | FixSource\[\] |  | Third-party pages the answers cite that were read and found stating the wrong value: domain, url, and a verbatim quote from the page. Empty when none was found; the fact then needs correcting on the business’s own surfaces. |

#### Guide

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `diagnosis` | string |  | Concise explanation of the diagnosed issue. |
| `gap_analysis` | string |  | Difference between the current and desired state. |
| `action_steps` | string\[\] |  | Ordered implementation steps. |
| `validation_steps` | string\[\] |  | Checks used to confirm completion. |
| `rollback_notes` | string \| null |  | Recovery guidance when a change must be reverted. |
| `impact_prediction` | string |  | Expected outcome after implementation. |
| `impact_timeline_days` | integer \| null |  | Estimated days before an outcome may become measurable. |
| `success_metrics` | SuccessMetric\[\] \| null |  | How success is measured for this action. Null when no measurable metric applies. |
| `execution_artifact` | ExecutionArtifact \| null |  | Ready-to-use copy the action steps refer to: page text, brief, message or checklist. |

#### SuccessMetric

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `metric` | string |  | What is measured. |
| `baseline` | string |  | Value before the action: plain words, or mentions over sampled AI answers such as "1/2". |
| `target_direction` | increase \| decrease \| maintain \| reach |  | Expected direction of the metric. |
| `evaluation_window_days` | integer |  | Days after completion before the metric is evaluated. |
| `verification_method` | string |  | How the result is checked. |

#### ExecutionArtifact

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `type` | content\_brief \| outreach\_template \| implementation\_checklist \| schema\_snippet |  | Kind of copy. |
| `title` | string |  | Short title of the copy. |
| `content` | string |  | The copy as plain text or Markdown. Bracketed placeholders such as \[opening hours\] are for the business to fill in. |
| `language` | string \| null |  | ISO 639-1 code when the copy is in a different language from the guide. |
| `email` | { to?: string, subject?: string, body: string } \| null |  | outreach\_template only: the pitch as an email. to is an address or a role such as "the editor". content holds the email body. |
| `sections` | { heading: string, content: string }\[\] \| null |  | content\_brief only: the page copy in parts (title tag, intro, FAQ…), each ready to paste. heading may be empty for an untitled intro. content holds the same copy as text. |
| `fields` | { name: string, value: string }\[\] \| null |  | Listing actions: the profile fields to set (description, category, hours…) and the value for each. content holds the same fields as text. |

#### TopicReference

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Topic identifier. |
| `name` | string |  | Topic display name. |

#### Pagination

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `page` | integer |  | Current 1-based page. |
| `per_page` | integer |  | Number of records requested per page. |
| `total` | integer |  | Total records matching the request. |
| `total_pages` | integer |  | Total available pages. |

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/actions?status=active&priority=high&page=1' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "actions": [
    {
      "id": "8ec60fe5-9c0b-41ea-98ce-9c98f846466f",
      "status": "in_progress",
      "priority": "high",
      "estimated_impact": 78,
      "expected_outcome": {
        "state": "modelled",
        "score": 78,
        "range": { "low": 31.0, "high": 77.5 },
        "unit": "percentage_points",
        "assumption": "Estimate based on current absence, lever fit, and feasibility."
      },
      "effort_level": "medium",
      "feasibility": "high",
      "source_category": "owned",
      "content_format": "comparison_page",
      "action_type": "content",
      "title": "Create a focused comparison page",
      "description": "Competitors are cited for high-intent comparison prompts.",
      "recommendation": "Publish a factual comparison addressing the observed gaps.",
      "target_kind": "site",
      "target": "https://example.com",
      "fixes": null,
      "topics": [
        {
          "id": "ab20526b-6bb2-436c-8c93-5bf77ea43848",
          "name": "AI visibility platforms"
        }
      ],
      "started_at": "2026-07-30T10:05:00Z",
      "completed_at": null,
      "measured_outcome": null,
      "dismissed_at": null,
      "resolved_at": null,
      "created_at": "2026-07-20T08:30:00Z",
      "updated_at": "2026-07-30T10:05:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total": 42,
    "total_pages": 2
  }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "details": { "status": ["is not supported"] },
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request |  | A path value, query parameter, or JSON body is malformed. |
| `401` | invalid\_api\_key |  | The Bearer API key is absent or invalid. |
| `403` | forbidden |  | The API key cannot perform this operation. |
| `404` | not\_found |  | Project, location, or action was not found. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Get action

`GET /projects/{project_id}/actions/{action_id}`

Returns one action, its structured implementation guide, and all supporting findings in the selected project.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `action_id` | uuid | Required | Action identifier. |

#### Action response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `action` | Action |  | Requested action and its implementation guide. |
| `findings` | Finding\[\] |  | Supporting findings linked to the action. |

#### Action

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique action identifier. |
| `status` | string |  | todo, in\_progress, completed, dismissed, or resolved. |
| `priority` | string |  | low, medium, high, or critical. |
| `expected_outcome` | object |  | How the impact was estimated. \`state\` is critical\_rule, modelled, directional, or unknown. \`score\` (0-100), \`range\`, and \`unit\` are only present when state is modelled; \`assumption\` explains why. Display "unknown" rather than a number for unknown and directional outcomes. |
| `estimated_impact` | integer |  | Deprecated: relative ranking score from 0 to 100, not a forecast. Show expected\_outcome to customers; this value is present even when no outcome can be modelled. |
| `effort_level` | low \| medium \| high |  | Estimated implementation effort. |
| `feasibility` | low \| medium \| high |  | Estimated ability to complete the recommendation. |
| `source_category` | string |  | owned, earned, reputation, local, competitive, operational, or general. |
| `content_format` | string \| null |  | Recommended deliverable format when relevant. |
| `action_type` | string |  | technical, content, visibility, competitor, listing, earned\_editorial, earned\_ugc, earned\_reference, sentiment\_correction, general, access, measurement\_fix, entity, facts\_page, claim\_parity, content\_extend, local\_template, reputation\_programme, marketplace\_listing, directory\_listing, list\_inclusion, partner\_integration, community, or site\_health. |
| `title` | string |  | Short action title. |
| `description` | string \| null |  | Problem or opportunity addressed by the action. |
| `recommendation` | string \| null |  | Recommended outcome or approach. |
| `target_kind` | string |  | Type of resource targeted by the action. |
| `target` | string \| null |  | Target URL, prompt reference, profile, or scope value. |
| `structured_target` | StructuredTarget \| null |  | Execution-ready target label, platform, URL, and state. |
| `dependencies` | ActionDependency\[\] |  | Actions that should be completed first. |
| `external_profile` | ExternalProfile \| null |  | URL and availability status for an external profile. |
| `diagnosis` | object \| null |  | Structured diagnosis behind the selected action when available. |
| `expected_lift` | object \| null |  | Directional expected-lift estimate and assumptions when available. |
| `validation` | Validation \| null |  | Metrics, success conditions, timing, and comparison used to validate the action. |
| `validation_result` | ValidationResult \| null |  | Result after validation has observed the completed action. |
| `evidence_summary` | object \| null |  | Bounded response, citation, and source evidence for the action. Each top\_sources row may include supports: up to 3 competitors that source backs in AI answers. |
| `fixes` | Fix\[\] \| null |  | Machine-readable corrections when the action comes from AI answers stating a business fact wrong: the stated value, the correct value, its source, and the third-party pages found carrying the wrong value. Null for every other action. |
| `guide` | Guide \| null |  | Structured implementation guide when available. |
| `topics` | TopicReference\[\] |  | Topics connected through supporting prompts and findings. |
| `started_at` | datetime \| null |  | When the action first entered in\_progress. |
| `completed_at` | datetime \| null |  | When the action was completed. |
| `measured_outcome` | MeasuredOutcome \| null |  | Targeted-prompt presence before and after completion. Present only when status is completed. Pending until the next visibility run can be compared. |
| `dismissed_at` | datetime \| null |  | When the action was dismissed. |
| `resolved_at` | datetime \| null |  | When supporting findings were automatically resolved. |
| `created_at` | datetime |  | Action creation time. |
| `updated_at` | datetime |  | Last action update time. |

#### StructuredTarget

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `label` | string \| null |  | Target display label. |
| `platform` | string \| null |  | Target platform. |
| `url` | URL \| null |  | Target URL. |
| `state` | existing\_page \| proposed\_page \| external\_destination \| scope \| null |  | How the target should be handled. |

#### ActionDependency

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `action_id` | uuid |  | Required action identifier. |
| `title` | string |  | Required action title. |
| `reason` | string \| null |  | Why it should be completed first. |

#### ExternalProfile

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | found \| readable \| unavailable \| not\_found \| unknown |  | Availability of the external profile. |
| `url` | URL \| null |  | External-profile URL. |

#### Validation

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `observation_window_days` | integer \| null |  | Days allowed to observe the expected result. |
| `comparison` | string \| null |  | Plain-language description of how the result is evaluated. |

#### ValidationResult

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | success \| failure \| inconclusive \| not\_observable |  | Outcome of the latest validation observation. |
| `reason` | string \| null |  | Explanation when validation did not succeed. |
| `observed_at` | datetime \| null |  | When the result was last observed. |
| `window_ends_at` | datetime \| null |  | When the observation window ends. |

#### MeasuredOutcome

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | string |  | pending or measured. |
| `reason` | string \| null |  | Present when pending: awaiting\_post\_completion\_run, no\_baseline\_run, no\_targeted\_prompts, or missing\_targeted\_executions. |
| `outcome` | string \| null |  | Present when measured: improved, unchanged, or declined. |
| `targeted` | object \| null |  | Presence on this action’s validation prompts: before, after, and sample\_size. |
| `other` | object \| null |  | Presence on all other prompts in the same runs: before, after, and sample\_size. |
| `baseline_at` | datetime \| null |  | When the pre-completion visibility run finished. |
| `comparison_at` | datetime \| null |  | When the post-completion visibility run finished. |

#### Fix

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `field` | phone \| website \| address \| hours \| closed |  | The business fact AI answers get wrong. |
| `qualifier` | string \| null |  | Weekday or weekday range for hours (monday … sunday, monday-saturday). Null for other fields. |
| `wrong` | string |  | The value AI answers state, normalized: HH:MM-HH:MM or closed for hours; E.164 for phone; permanently, temporarily, or moved for closed. |
| `correct` | string \| null |  | The value on record, normalized the same way. |
| `correct_source` | website \| location\_record \| google\_business\_profile \| customer \| null |  | Where the correct value comes from. |
| `stated_by` | string\[\] |  | AI models whose answers state the wrong value. |
| `carried_by` | FixSource\[\] |  | Third-party pages the answers cite that were read and found stating the wrong value: domain, url, and a verbatim quote from the page. Empty when none was found; the fact then needs correcting on the business’s own surfaces. |

#### Guide

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `diagnosis` | string |  | Concise explanation of the diagnosed issue. |
| `gap_analysis` | string |  | Difference between the current and desired state. |
| `action_steps` | string\[\] |  | Ordered implementation steps. |
| `validation_steps` | string\[\] |  | Checks used to confirm completion. |
| `rollback_notes` | string \| null |  | Recovery guidance when a change must be reverted. |
| `impact_prediction` | string |  | Expected outcome after implementation. |
| `impact_timeline_days` | integer \| null |  | Estimated days before an outcome may become measurable. |
| `success_metrics` | SuccessMetric\[\] \| null |  | How success is measured for this action. Null when no measurable metric applies. |
| `execution_artifact` | ExecutionArtifact \| null |  | Ready-to-use copy the action steps refer to: page text, brief, message or checklist. |

#### SuccessMetric

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `metric` | string |  | What is measured. |
| `baseline` | string |  | Value before the action: plain words, or mentions over sampled AI answers such as "1/2". |
| `target_direction` | increase \| decrease \| maintain \| reach |  | Expected direction of the metric. |
| `evaluation_window_days` | integer |  | Days after completion before the metric is evaluated. |
| `verification_method` | string |  | How the result is checked. |

#### ExecutionArtifact

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `type` | content\_brief \| outreach\_template \| implementation\_checklist \| schema\_snippet |  | Kind of copy. |
| `title` | string |  | Short title of the copy. |
| `content` | string |  | The copy as plain text or Markdown. Bracketed placeholders such as \[opening hours\] are for the business to fill in. |
| `language` | string \| null |  | ISO 639-1 code when the copy is in a different language from the guide. |
| `email` | { to?: string, subject?: string, body: string } \| null |  | outreach\_template only: the pitch as an email. to is an address or a role such as "the editor". content holds the email body. |
| `sections` | { heading: string, content: string }\[\] \| null |  | content\_brief only: the page copy in parts (title tag, intro, FAQ…), each ready to paste. heading may be empty for an untitled intro. content holds the same copy as text. |
| `fields` | { name: string, value: string }\[\] \| null |  | Listing actions: the profile fields to set (description, category, hours…) and the value for each. content holds the same fields as text. |

#### TopicReference

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Topic identifier. |
| `name` | string |  | Topic display name. |

#### Finding

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `source` | string |  | Analysis source that produced the finding. |
| `category` | string |  | Finding category. |
| `severity` | string |  | Finding severity. |
| `status` | string |  | Current finding lifecycle status. |
| `target_kind` | string |  | Type of resource that produced the signal. |
| `target` | string \| null |  | Target value when available. |
| `title` | string |  | Finding title. |
| `description` | string \| null |  | Evidence-backed finding description. |
| `recommendation` | string \| null |  | Recommended response to the finding. |
| `first_seen_at` | datetime |  | First detection time. |
| `last_seen_at` | datetime |  | Most recent detection time. |
| `resolved_at` | datetime \| null |  | Resolution time, when resolved. |

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/actions/{action_id}' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "action": {
    "id": "8ec60fe5-9c0b-41ea-98ce-9c98f846466f",
    "status": "in_progress",
    "priority": "high",
    "estimated_impact": 64,
    "effort_level": "low",
    "feasibility": "high",
    "source_category": "general",
    "content_format": null,
    "action_type": "directory_listing",
    "title": "Correct the Sunday opening hours on CityList",
    "description": "ChatGPT and Gemini tell customers the branch opens 09:00-19:00 on Sundays; the Google Business Profile states 10:00-18:00. The CityList page both assistants cite carries the wrong hours.",
    "recommendation": "Update the CityList listing to 10:00-18:00 on Sundays and confirm the hours match the Google Business Profile.",
    "target_kind": "domain",
    "target": "https://citylist.example/acme-phoenix",
    "structured_target": {
      "label": "CityList",
      "platform": "citylist.example",
      "url": "https://citylist.example/acme-phoenix",
      "state": "external_destination"
    },
    "external_profile": {
      "status": "readable",
      "url": "https://citylist.example/acme-phoenix"
    },
    "guide": {
      "diagnosis": "ChatGPT and Gemini cite the CityList page for Sunday hours, and it still lists 09:00-19:00.",
      "gap_analysis": "The Google Business Profile states 10:00-18:00 on Sundays; CityList is the only cited source with different hours.",
      "action_steps": [
        "Sign in to the CityList business dashboard for the Phoenix branch.",
        "Set the Sunday hours to 10:00-18:00 and save.",
        "Check that the public listing page shows the new hours."
      ],
      "validation_steps": [
        "Open https://citylist.example/acme-phoenix and confirm Sunday shows 10:00-18:00."
      ],
      "rollback_notes": null,
      "impact_prediction": "AI answers that cite CityList state the correct Sunday hours.",
      "impact_timeline_days": 21,
      "success_metrics": [
        {
          "metric": "AI answers stating the correct Sunday hours",
          "baseline": "ChatGPT 09:00-19:00, Gemini 09:00-19:00",
          "target_direction": "reach",
          "evaluation_window_days": 21,
          "verification_method": "Re-run the tracked prompts and compare the stated hours with the Google Business Profile."
        }
      ],
      "execution_artifact": {
        "type": "implementation_checklist",
        "title": "CityList hours update",
        "content": "Sunday: 10:00-18:00",
        "language": null,
        "email": null,
        "sections": null,
        "fields": [
          { "name": "Sunday hours", "value": "10:00-18:00" }
        ]
      }
    },
    "fixes": [
      {
        "field": "hours",
        "qualifier": "sunday",
        "wrong": "09:00-19:00",
        "correct": "10:00-18:00",
        "correct_source": "google_business_profile",
        "stated_by": ["chatgpt", "gemini"],
        "carried_by": [
          {
            "domain": "citylist.example",
            "url": "https://citylist.example/acme-phoenix",
            "quote": "Sunday 9:00 - 19:00"
          }
        ]
      }
    ],
    "topics": [],
    "started_at": "2026-07-30T10:05:00Z",
    "completed_at": null,
    "measured_outcome": null,
    "dismissed_at": null,
    "resolved_at": null,
    "created_at": "2026-07-20T08:30:00Z",
    "updated_at": "2026-07-30T10:05:00Z"
  },
  "findings": [
    {
      "source": "signals",
      "category": "fact_drift",
      "severity": "medium",
      "status": "open",
      "target_kind": "page",
      "target": "https://example.com/phoenix",
      "title": "AI answers state different opening hours",
      "description": "chatgpt and gemini give 09:00-19:00 for Sunday; the Google Business Profile states 10:00-18:00.",
      "recommendation": "Publish the correct Sunday hours (10:00-18:00) consistently on the website, the Google Business Profile and the listings AI assistants read, and correct the sources that carry 09:00-19:00.",
      "first_seen_at": "2026-07-10T07:30:00Z",
      "last_seen_at": "2026-07-30T09:30:00Z",
      "resolved_at": null
    }
  ]
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "details": { "status": ["is not supported"] },
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request |  | A path value, query parameter, or JSON body is malformed. |
| `401` | invalid\_api\_key |  | The Bearer API key is absent or invalid. |
| `403` | forbidden |  | The API key cannot perform this operation. |
| `404` | not\_found |  | Project, location, or action was not found. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Update action status

`PATCH /projects/{project_id}/actions/{action_id}`

Updates the customer-managed lifecycle status of an action. Resolved actions are controlled automatically by their supporting findings.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `action_id` | uuid | Required | Action identifier. |

#### Request body

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | todo \| in\_progress \| completed \| dismissed |  | New customer-managed action status. |

#### Allowed transitions

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `todo` | → in\_progress, completed, dismissed |  | A new action can be started, completed, or dismissed. |
| `in_progress` | → todo, completed, dismissed |  | Work can be paused, completed, or dismissed. |
| `completed` | → todo |  | A completed action can be reopened. |
| `dismissed` | → todo |  | A dismissed action can be reopened. |

#### Action response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `action` | Action |  | Updated action. |

#### Action

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique action identifier. |
| `status` | string |  | todo, in\_progress, completed, dismissed, or resolved. |
| `priority` | string |  | low, medium, high, or critical. |
| `expected_outcome` | object |  | How the impact was estimated. \`state\` is critical\_rule, modelled, directional, or unknown. \`score\` (0-100), \`range\`, and \`unit\` are only present when state is modelled; \`assumption\` explains why. Display "unknown" rather than a number for unknown and directional outcomes. |
| `estimated_impact` | integer |  | Deprecated: relative ranking score from 0 to 100, not a forecast. Show expected\_outcome to customers; this value is present even when no outcome can be modelled. |
| `effort_level` | low \| medium \| high |  | Estimated implementation effort. |
| `feasibility` | low \| medium \| high |  | Estimated ability to complete the recommendation. |
| `source_category` | string |  | owned, earned, reputation, local, competitive, operational, or general. |
| `content_format` | string \| null |  | Recommended deliverable format when relevant. |
| `action_type` | string |  | technical, content, visibility, competitor, listing, earned\_editorial, earned\_ugc, earned\_reference, sentiment\_correction, general, access, measurement\_fix, entity, facts\_page, claim\_parity, content\_extend, local\_template, reputation\_programme, marketplace\_listing, directory\_listing, list\_inclusion, partner\_integration, community, or site\_health. |
| `title` | string |  | Short action title. |
| `description` | string \| null |  | Problem or opportunity addressed by the action. |
| `recommendation` | string \| null |  | Recommended outcome or approach. |
| `target_kind` | string |  | Type of resource targeted by the action. |
| `target` | string \| null |  | Target URL, prompt reference, profile, or scope value. |
| `structured_target` | StructuredTarget \| null |  | Execution-ready target label, platform, URL, and state. |
| `dependencies` | ActionDependency\[\] |  | Actions that should be completed first. |
| `external_profile` | ExternalProfile \| null |  | URL and availability status for an external profile. |
| `diagnosis` | object \| null |  | Structured diagnosis behind the selected action when available. |
| `expected_lift` | object \| null |  | Directional expected-lift estimate and assumptions when available. |
| `validation` | Validation \| null |  | Metrics, success conditions, timing, and comparison used to validate the action. |
| `validation_result` | ValidationResult \| null |  | Result after validation has observed the completed action. |
| `evidence_summary` | object \| null |  | Bounded response, citation, and source evidence for the action. Each top\_sources row may include supports: up to 3 competitors that source backs in AI answers. |
| `fixes` | Fix\[\] \| null |  | Machine-readable corrections when the action comes from AI answers stating a business fact wrong: the stated value, the correct value, its source, and the third-party pages found carrying the wrong value. Null for every other action. |
| `guide` | Guide \| null |  | Structured implementation guide when available. |
| `topics` | TopicReference\[\] |  | Topics connected through supporting prompts and findings. |
| `started_at` | datetime \| null |  | When the action first entered in\_progress. |
| `completed_at` | datetime \| null |  | When the action was completed. |
| `measured_outcome` | MeasuredOutcome \| null |  | Targeted-prompt presence before and after completion. Present only when status is completed. Pending until the next visibility run can be compared. |
| `dismissed_at` | datetime \| null |  | When the action was dismissed. |
| `resolved_at` | datetime \| null |  | When supporting findings were automatically resolved. |
| `created_at` | datetime |  | Action creation time. |
| `updated_at` | datetime |  | Last action update time. |

#### StructuredTarget

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `label` | string \| null |  | Target display label. |
| `platform` | string \| null |  | Target platform. |
| `url` | URL \| null |  | Target URL. |
| `state` | existing\_page \| proposed\_page \| external\_destination \| scope \| null |  | How the target should be handled. |

#### ActionDependency

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `action_id` | uuid |  | Required action identifier. |
| `title` | string |  | Required action title. |
| `reason` | string \| null |  | Why it should be completed first. |

#### ExternalProfile

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | found \| readable \| unavailable \| not\_found \| unknown |  | Availability of the external profile. |
| `url` | URL \| null |  | External-profile URL. |

#### Validation

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `observation_window_days` | integer \| null |  | Days allowed to observe the expected result. |
| `comparison` | string \| null |  | Plain-language description of how the result is evaluated. |

#### ValidationResult

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | success \| failure \| inconclusive \| not\_observable |  | Outcome of the latest validation observation. |
| `reason` | string \| null |  | Explanation when validation did not succeed. |
| `observed_at` | datetime \| null |  | When the result was last observed. |
| `window_ends_at` | datetime \| null |  | When the observation window ends. |

#### MeasuredOutcome

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | string |  | pending or measured. |
| `reason` | string \| null |  | Present when pending: awaiting\_post\_completion\_run, no\_baseline\_run, no\_targeted\_prompts, or missing\_targeted\_executions. |
| `outcome` | string \| null |  | Present when measured: improved, unchanged, or declined. |
| `targeted` | object \| null |  | Presence on this action’s validation prompts: before, after, and sample\_size. |
| `other` | object \| null |  | Presence on all other prompts in the same runs: before, after, and sample\_size. |
| `baseline_at` | datetime \| null |  | When the pre-completion visibility run finished. |
| `comparison_at` | datetime \| null |  | When the post-completion visibility run finished. |

#### Fix

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `field` | phone \| website \| address \| hours \| closed |  | The business fact AI answers get wrong. |
| `qualifier` | string \| null |  | Weekday or weekday range for hours (monday … sunday, monday-saturday). Null for other fields. |
| `wrong` | string |  | The value AI answers state, normalized: HH:MM-HH:MM or closed for hours; E.164 for phone; permanently, temporarily, or moved for closed. |
| `correct` | string \| null |  | The value on record, normalized the same way. |
| `correct_source` | website \| location\_record \| google\_business\_profile \| customer \| null |  | Where the correct value comes from. |
| `stated_by` | string\[\] |  | AI models whose answers state the wrong value. |
| `carried_by` | FixSource\[\] |  | Third-party pages the answers cite that were read and found stating the wrong value: domain, url, and a verbatim quote from the page. Empty when none was found; the fact then needs correcting on the business’s own surfaces. |

#### Guide

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `diagnosis` | string |  | Concise explanation of the diagnosed issue. |
| `gap_analysis` | string |  | Difference between the current and desired state. |
| `action_steps` | string\[\] |  | Ordered implementation steps. |
| `validation_steps` | string\[\] |  | Checks used to confirm completion. |
| `rollback_notes` | string \| null |  | Recovery guidance when a change must be reverted. |
| `impact_prediction` | string |  | Expected outcome after implementation. |
| `impact_timeline_days` | integer \| null |  | Estimated days before an outcome may become measurable. |
| `success_metrics` | SuccessMetric\[\] \| null |  | How success is measured for this action. Null when no measurable metric applies. |
| `execution_artifact` | ExecutionArtifact \| null |  | Ready-to-use copy the action steps refer to: page text, brief, message or checklist. |

#### SuccessMetric

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `metric` | string |  | What is measured. |
| `baseline` | string |  | Value before the action: plain words, or mentions over sampled AI answers such as "1/2". |
| `target_direction` | increase \| decrease \| maintain \| reach |  | Expected direction of the metric. |
| `evaluation_window_days` | integer |  | Days after completion before the metric is evaluated. |
| `verification_method` | string |  | How the result is checked. |

#### ExecutionArtifact

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `type` | content\_brief \| outreach\_template \| implementation\_checklist \| schema\_snippet |  | Kind of copy. |
| `title` | string |  | Short title of the copy. |
| `content` | string |  | The copy as plain text or Markdown. Bracketed placeholders such as \[opening hours\] are for the business to fill in. |
| `language` | string \| null |  | ISO 639-1 code when the copy is in a different language from the guide. |
| `email` | { to?: string, subject?: string, body: string } \| null |  | outreach\_template only: the pitch as an email. to is an address or a role such as "the editor". content holds the email body. |
| `sections` | { heading: string, content: string }\[\] \| null |  | content\_brief only: the page copy in parts (title tag, intro, FAQ…), each ready to paste. heading may be empty for an untitled intro. content holds the same copy as text. |
| `fields` | { name: string, value: string }\[\] \| null |  | Listing actions: the profile fields to set (description, category, hours…) and the value for each. content holds the same fields as text. |

#### TopicReference

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Topic identifier. |
| `name` | string |  | Topic display name. |

#### Request and response

```curl
curl --request PATCH \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/actions/{action_id}' \
  --header 'Authorization: Bearer ceyo_platform_...' \
  --header 'Content-Type: application/json' \
  --data '{"status":"completed"}'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "action": {
    "id": "8ec60fe5-9c0b-41ea-98ce-9c98f846466f",
    "status": "completed",
    "completed_at": "2026-07-31T10:15:00Z",
    "measured_outcome": {
      "status": "pending",
      "reason": "awaiting_post_completion_run"
    },
    "updated_at": "2026-07-31T10:15:00Z"
  }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "details": { "status": ["is not supported"] },
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request |  | A path value, query parameter, or JSON body is malformed. |
| `401` | invalid\_api\_key |  | The Bearer API key is absent or invalid. |
| `403` | forbidden |  | The API key cannot perform this operation. |
| `404` | not\_found |  | Project, location, or action was not found. |
| `409` | invalid\_transition |  | The requested status transition is not allowed. |
| `422` | invalid\_status |  | The submitted status is not writable. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Get action impact

`GET /projects/{project_id}/actions/impact`

Compares visibility signals before and after completed actions in the selected project. Actions remain pending until both baseline and comparison runs are available.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |

#### Query parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `start_on` | date | Optional; Default: 90 days before end\_on | First completion date to include. |
| `end_on` | date | Optional; Default: Today | Last completion date to include. |
| `topic_id` | uuid | Optional | Measure actions connected to one topic. |
| `action_type` | string | Optional | Measure one action type. |

#### Response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `period` | ImpactPeriod |  | Resolved start\_on and end\_on. |
| `summary` | ImpactSummary |  | Completed, measured, pending, and outcome totals. |
| `timeline` | ImpactTimelinePoint\[\] |  | Weekly completion and visibility outcome series. |
| `actions` | ActionMeasurement\[\] |  | Measurements for up to 100 completed actions. |

#### ImpactPeriod

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `start_on` | date |  | First completion date included in the measurement window. |
| `end_on` | date |  | Last completion date included in the measurement window. |

#### ImpactSummary

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `completed_actions` | integer |  | Completed actions included in the measurement window. |
| `measured_actions` | integer |  | Actions with both baseline and comparison signals. |
| `pending_actions` | integer |  | Actions awaiting an eligible baseline or comparison signal. |
| `improved` | integer |  | Measured actions classified as improved. |
| `unchanged` | integer |  | Measured actions classified as unchanged. |
| `declined` | integer |  | Measured actions classified as declined. |
| `average_visibility_delta` | number \| null |  | Mean visibility percentage-point change, or null when no action is measured. |
| `average_citation_delta` | number \| null |  | Mean citation-count change, or null when no action is measured. |
| `average_sentiment_delta` | number \| null |  | Mean sentiment-score change, or null when no comparable values exist. |

#### ImpactTimelinePoint

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `date` | date |  | Start date of the represented week. |
| `completed` | integer |  | Actions completed during the represented week. |
| `measured` | integer |  | Completed actions with an available measurement. |
| `average_visibility_delta` | number \| null |  | Mean visibility percentage-point change, or null when the week has no measurements. |

#### ActionMeasurement

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Action identifier. |
| `title` | string |  | Action title. |
| `action_type` | string |  | Action type. |
| `completed_at` | datetime |  | Action completion time. |
| `topics` | TopicReference\[\] |  | Topics used to scope the measurement. |
| `status` | measured \| pending |  | Measurement availability. |
| `reason` | no\_baseline\_run \| awaiting\_post\_completion\_run \| null |  | Why a pending action cannot yet be measured. |
| `outcome` | improved \| unchanged \| declined \| null |  | Outcome derived from visibility change when measured. |
| `baseline_at` | datetime \| null |  | Baseline run completion time. |
| `comparison_at` | datetime \| null |  | Comparison run completion time. |
| `baseline` | ImpactSnapshot \| null |  | Signals immediately before completion when measured. |
| `comparison` | ImpactSnapshot \| null |  | Latest eligible signals after completion when measured. |
| `delta` | ActionMeasurementDelta \| null |  | Signal changes when the action is measured. |

#### ActionMeasurementDelta

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `visibility_percentage_points` | number \| null |  | Comparison visibility minus baseline visibility. |
| `citation_count` | integer \| null |  | Comparison citation count minus baseline citation count. |
| `sentiment_score` | number \| null |  | Comparison sentiment score minus baseline sentiment score. |
| `average_position` | number \| null |  | Improvement in average position; positive values indicate movement toward position one. |

#### ImpactSnapshot

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `visibility_percentage` | number |  | Primary brand visibility percentage. |
| `citation_count` | integer |  | Distinct cited pages. |
| `sentiment_score` | number \| null |  | Average primary-brand sentiment score. |
| `average_position` | number \| null |  | Average primary-brand position. |

#### TopicReference

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Topic identifier. |
| `name` | string |  | Topic display name. |

> **Outcome interpretation**
>
> Measurements show correlated before-and-after signals. They do not establish that an action was the only cause of a change.

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/actions/impact?start_on=2026-05-01&end_on=2026-07-30' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "period": {
    "start_on": "2026-05-01",
    "end_on": "2026-07-30"
  },
  "summary": {
    "completed_actions": 12,
    "measured_actions": 9,
    "pending_actions": 3,
    "improved": 6,
    "unchanged": 2,
    "declined": 1,
    "average_visibility_delta": 4.8,
    "average_citation_delta": 3.2,
    "average_sentiment_delta": 0.4
  },
  "timeline": [
    {
      "date": "2026-07-27",
      "completed": 3,
      "measured": 2,
      "average_visibility_delta": 5.1
    }
  ],
  "actions": [
    {
      "id": "8ec60fe5-9c0b-41ea-98ce-9c98f846466f",
      "title": "Create a focused comparison page",
      "action_type": "content",
      "completed_at": "2026-07-10T10:15:00Z",
      "topics": [],
      "status": "measured",
      "outcome": "improved",
      "baseline_at": "2026-07-09T07:30:00Z",
      "comparison_at": "2026-07-30T09:30:00Z",
      "baseline": {
        "visibility_percentage": 35.0,
        "citation_count": 8,
        "sentiment_score": 6.8,
        "average_position": 3.4
      },
      "comparison": {
        "visibility_percentage": 42.5,
        "citation_count": 13,
        "sentiment_score": 7.3,
        "average_position": 2.8
      },
      "delta": {
        "visibility_percentage_points": 7.5,
        "citation_count": 5,
        "sentiment_score": 0.5,
        "average_position": 0.6
      }
    }
  ]
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "details": { "status": ["is not supported"] },
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request |  | A path value, query parameter, or JSON body is malformed. |
| `401` | invalid\_api\_key |  | The Bearer API key is absent or invalid. |
| `403` | forbidden |  | The API key cannot perform this operation. |
| `404` | not\_found |  | Project, location, or action was not found. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

## Location scope

### Actions

Retrieve prioritized recommendations, supporting findings, implementation guides, status, and measured outcomes.

**Scope:** Location scope — `/projects/{project_id}/locations/{location_id}/…`

### List actions

`GET /projects/{project_id}/locations/{location_id}/actions`

Returns actions for the selected location, with server-side filtering and pagination.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `location_id` | location UUID \| location external ID | Required | Location identifier. |

#### Query parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `q` | string | Optional | Search titles, descriptions, recommendations, and targets. |
| `status` | string | Optional; Default: active | active, todo, in\_progress, completed, dismissed, or resolved. active includes todo and in\_progress. |
| `priority` | string | Optional | low, medium, high, or critical. |
| `action_type` | string | Optional | Restrict results to one action type. |
| `effort_level` | low \| medium \| high | Optional | Restrict results by estimated effort. |
| `source_category` | string | Optional | Restrict results by work area. |
| `topic_id` | uuid | Optional | Return actions linked to one topic. |
| `page` | integer | Optional; Default: 1 | The 1-based page number. |
| `per_page` | integer | Optional; Default: 25 | Number of actions per page. Maximum: 100. |

#### Response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `location_id` | uuid |  | Resolved Ceyo location identifier. |
| `actions` | Action\[\] |  | Actions matching the selected filters. |
| `pagination` | Pagination |  | Pagination metadata. |

#### Action

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique action identifier. |
| `status` | string |  | todo, in\_progress, completed, dismissed, or resolved. |
| `priority` | string |  | low, medium, high, or critical. |
| `expected_outcome` | object |  | How the impact was estimated. \`state\` is critical\_rule, modelled, directional, or unknown. \`score\` (0-100), \`range\`, and \`unit\` are only present when state is modelled; \`assumption\` explains why. Display "unknown" rather than a number for unknown and directional outcomes. |
| `estimated_impact` | integer |  | Deprecated: relative ranking score from 0 to 100, not a forecast. Show expected\_outcome to customers; this value is present even when no outcome can be modelled. |
| `effort_level` | low \| medium \| high |  | Estimated implementation effort. |
| `feasibility` | low \| medium \| high |  | Estimated ability to complete the recommendation. |
| `source_category` | string |  | owned, earned, reputation, local, competitive, operational, or general. |
| `content_format` | string \| null |  | Recommended deliverable format when relevant. |
| `action_type` | string |  | technical, content, visibility, competitor, listing, earned\_editorial, earned\_ugc, earned\_reference, sentiment\_correction, general, access, measurement\_fix, entity, facts\_page, claim\_parity, content\_extend, local\_template, reputation\_programme, marketplace\_listing, directory\_listing, list\_inclusion, partner\_integration, community, or site\_health. |
| `title` | string |  | Short action title. |
| `description` | string \| null |  | Problem or opportunity addressed by the action. |
| `recommendation` | string \| null |  | Recommended outcome or approach. |
| `target_kind` | string |  | Type of resource targeted by the action. |
| `target` | string \| null |  | Target URL, prompt reference, profile, or scope value. |
| `structured_target` | StructuredTarget \| null |  | Execution-ready target label, platform, URL, and state. |
| `dependencies` | ActionDependency\[\] |  | Actions that should be completed first. |
| `external_profile` | ExternalProfile \| null |  | URL and availability status for an external profile. |
| `diagnosis` | object \| null |  | Structured diagnosis behind the selected action when available. |
| `expected_lift` | object \| null |  | Directional expected-lift estimate and assumptions when available. |
| `validation` | Validation \| null |  | Metrics, success conditions, timing, and comparison used to validate the action. |
| `validation_result` | ValidationResult \| null |  | Result after validation has observed the completed action. |
| `evidence_summary` | object \| null |  | Bounded response, citation, and source evidence for the action. Each top\_sources row may include supports: up to 3 competitors that source backs in AI answers. |
| `fixes` | Fix\[\] \| null |  | Machine-readable corrections when the action comes from AI answers stating a business fact wrong: the stated value, the correct value, its source, and the third-party pages found carrying the wrong value. Null for every other action. |
| `guide` | Guide \| null |  | Structured implementation guide when available. |
| `topics` | TopicReference\[\] |  | Topics connected through supporting prompts and findings. |
| `started_at` | datetime \| null |  | When the action first entered in\_progress. |
| `completed_at` | datetime \| null |  | When the action was completed. |
| `measured_outcome` | MeasuredOutcome \| null |  | Targeted-prompt presence before and after completion. Present only when status is completed. Pending until the next visibility run can be compared. |
| `dismissed_at` | datetime \| null |  | When the action was dismissed. |
| `resolved_at` | datetime \| null |  | When supporting findings were automatically resolved. |
| `created_at` | datetime |  | Action creation time. |
| `updated_at` | datetime |  | Last action update time. |

#### StructuredTarget

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `label` | string \| null |  | Target display label. |
| `platform` | string \| null |  | Target platform. |
| `url` | URL \| null |  | Target URL. |
| `state` | existing\_page \| proposed\_page \| external\_destination \| scope \| null |  | How the target should be handled. |

#### ActionDependency

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `action_id` | uuid |  | Required action identifier. |
| `title` | string |  | Required action title. |
| `reason` | string \| null |  | Why it should be completed first. |

#### ExternalProfile

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | found \| readable \| unavailable \| not\_found \| unknown |  | Availability of the external profile. |
| `url` | URL \| null |  | External-profile URL. |

#### Validation

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `observation_window_days` | integer \| null |  | Days allowed to observe the expected result. |
| `comparison` | string \| null |  | Plain-language description of how the result is evaluated. |

#### ValidationResult

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | success \| failure \| inconclusive \| not\_observable |  | Outcome of the latest validation observation. |
| `reason` | string \| null |  | Explanation when validation did not succeed. |
| `observed_at` | datetime \| null |  | When the result was last observed. |
| `window_ends_at` | datetime \| null |  | When the observation window ends. |

#### MeasuredOutcome

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | string |  | pending or measured. |
| `reason` | string \| null |  | Present when pending: awaiting\_post\_completion\_run, no\_baseline\_run, no\_targeted\_prompts, or missing\_targeted\_executions. |
| `outcome` | string \| null |  | Present when measured: improved, unchanged, or declined. |
| `targeted` | object \| null |  | Presence on this action’s validation prompts: before, after, and sample\_size. |
| `other` | object \| null |  | Presence on all other prompts in the same runs: before, after, and sample\_size. |
| `baseline_at` | datetime \| null |  | When the pre-completion visibility run finished. |
| `comparison_at` | datetime \| null |  | When the post-completion visibility run finished. |

#### Fix

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `field` | phone \| website \| address \| hours \| closed |  | The business fact AI answers get wrong. |
| `qualifier` | string \| null |  | Weekday or weekday range for hours (monday … sunday, monday-saturday). Null for other fields. |
| `wrong` | string |  | The value AI answers state, normalized: HH:MM-HH:MM or closed for hours; E.164 for phone; permanently, temporarily, or moved for closed. |
| `correct` | string \| null |  | The value on record, normalized the same way. |
| `correct_source` | website \| location\_record \| google\_business\_profile \| customer \| null |  | Where the correct value comes from. |
| `stated_by` | string\[\] |  | AI models whose answers state the wrong value. |
| `carried_by` | FixSource\[\] |  | Third-party pages the answers cite that were read and found stating the wrong value: domain, url, and a verbatim quote from the page. Empty when none was found; the fact then needs correcting on the business’s own surfaces. |

#### Guide

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `diagnosis` | string |  | Concise explanation of the diagnosed issue. |
| `gap_analysis` | string |  | Difference between the current and desired state. |
| `action_steps` | string\[\] |  | Ordered implementation steps. |
| `validation_steps` | string\[\] |  | Checks used to confirm completion. |
| `rollback_notes` | string \| null |  | Recovery guidance when a change must be reverted. |
| `impact_prediction` | string |  | Expected outcome after implementation. |
| `impact_timeline_days` | integer \| null |  | Estimated days before an outcome may become measurable. |
| `success_metrics` | SuccessMetric\[\] \| null |  | How success is measured for this action. Null when no measurable metric applies. |
| `execution_artifact` | ExecutionArtifact \| null |  | Ready-to-use copy the action steps refer to: page text, brief, message or checklist. |

#### SuccessMetric

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `metric` | string |  | What is measured. |
| `baseline` | string |  | Value before the action: plain words, or mentions over sampled AI answers such as "1/2". |
| `target_direction` | increase \| decrease \| maintain \| reach |  | Expected direction of the metric. |
| `evaluation_window_days` | integer |  | Days after completion before the metric is evaluated. |
| `verification_method` | string |  | How the result is checked. |

#### ExecutionArtifact

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `type` | content\_brief \| outreach\_template \| implementation\_checklist \| schema\_snippet |  | Kind of copy. |
| `title` | string |  | Short title of the copy. |
| `content` | string |  | The copy as plain text or Markdown. Bracketed placeholders such as \[opening hours\] are for the business to fill in. |
| `language` | string \| null |  | ISO 639-1 code when the copy is in a different language from the guide. |
| `email` | { to?: string, subject?: string, body: string } \| null |  | outreach\_template only: the pitch as an email. to is an address or a role such as "the editor". content holds the email body. |
| `sections` | { heading: string, content: string }\[\] \| null |  | content\_brief only: the page copy in parts (title tag, intro, FAQ…), each ready to paste. heading may be empty for an untitled intro. content holds the same copy as text. |
| `fields` | { name: string, value: string }\[\] \| null |  | Listing actions: the profile fields to set (description, category, hours…) and the value for each. content holds the same fields as text. |

#### TopicReference

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Topic identifier. |
| `name` | string |  | Topic display name. |

#### Pagination

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `page` | integer |  | Current 1-based page. |
| `per_page` | integer |  | Number of records requested per page. |
| `total` | integer |  | Total records matching the request. |
| `total_pages` | integer |  | Total available pages. |

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/locations/{location_id}/actions?status=active&priority=high&page=1' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "location_id": "a1308d14-149c-4dd7-a4c5-295ac9090f58",
  "actions": [
    {
      "id": "8ec60fe5-9c0b-41ea-98ce-9c98f846466f",
      "status": "in_progress",
      "priority": "high",
      "estimated_impact": 78,
      "expected_outcome": {
        "state": "modelled",
        "score": 78,
        "range": { "low": 31.0, "high": 77.5 },
        "unit": "percentage_points",
        "assumption": "Estimate based on current absence, lever fit, and feasibility."
      },
      "effort_level": "medium",
      "feasibility": "high",
      "source_category": "owned",
      "content_format": "comparison_page",
      "action_type": "content",
      "title": "Create a focused comparison page",
      "description": "Competitors are cited for high-intent comparison prompts.",
      "recommendation": "Publish a factual comparison addressing the observed gaps.",
      "target_kind": "site",
      "target": "https://example.com",
      "fixes": null,
      "topics": [
        {
          "id": "ab20526b-6bb2-436c-8c93-5bf77ea43848",
          "name": "AI visibility platforms"
        }
      ],
      "started_at": "2026-07-30T10:05:00Z",
      "completed_at": null,
      "measured_outcome": null,
      "dismissed_at": null,
      "resolved_at": null,
      "created_at": "2026-07-20T08:30:00Z",
      "updated_at": "2026-07-30T10:05:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 25,
    "total": 42,
    "total_pages": 2
  }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "details": { "status": ["is not supported"] },
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request |  | A path value, query parameter, or JSON body is malformed. |
| `401` | invalid\_api\_key |  | The Bearer API key is absent or invalid. |
| `403` | forbidden |  | The API key cannot perform this operation. |
| `404` | not\_found |  | Project, location, or action was not found. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Get action

`GET /projects/{project_id}/locations/{location_id}/actions/{action_id}`

Returns one action, its structured implementation guide, and all supporting findings in the selected location.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `location_id` | location UUID \| location external ID | Required | Location identifier. |
| `action_id` | uuid | Required | Action identifier. |

#### Action response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `location_id` | uuid |  | Resolved Ceyo location identifier. |
| `action` | Action |  | Requested action and its implementation guide. |
| `findings` | Finding\[\] |  | Supporting findings linked to the action. |

#### Action

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique action identifier. |
| `status` | string |  | todo, in\_progress, completed, dismissed, or resolved. |
| `priority` | string |  | low, medium, high, or critical. |
| `expected_outcome` | object |  | How the impact was estimated. \`state\` is critical\_rule, modelled, directional, or unknown. \`score\` (0-100), \`range\`, and \`unit\` are only present when state is modelled; \`assumption\` explains why. Display "unknown" rather than a number for unknown and directional outcomes. |
| `estimated_impact` | integer |  | Deprecated: relative ranking score from 0 to 100, not a forecast. Show expected\_outcome to customers; this value is present even when no outcome can be modelled. |
| `effort_level` | low \| medium \| high |  | Estimated implementation effort. |
| `feasibility` | low \| medium \| high |  | Estimated ability to complete the recommendation. |
| `source_category` | string |  | owned, earned, reputation, local, competitive, operational, or general. |
| `content_format` | string \| null |  | Recommended deliverable format when relevant. |
| `action_type` | string |  | technical, content, visibility, competitor, listing, earned\_editorial, earned\_ugc, earned\_reference, sentiment\_correction, general, access, measurement\_fix, entity, facts\_page, claim\_parity, content\_extend, local\_template, reputation\_programme, marketplace\_listing, directory\_listing, list\_inclusion, partner\_integration, community, or site\_health. |
| `title` | string |  | Short action title. |
| `description` | string \| null |  | Problem or opportunity addressed by the action. |
| `recommendation` | string \| null |  | Recommended outcome or approach. |
| `target_kind` | string |  | Type of resource targeted by the action. |
| `target` | string \| null |  | Target URL, prompt reference, profile, or scope value. |
| `structured_target` | StructuredTarget \| null |  | Execution-ready target label, platform, URL, and state. |
| `dependencies` | ActionDependency\[\] |  | Actions that should be completed first. |
| `external_profile` | ExternalProfile \| null |  | URL and availability status for an external profile. |
| `diagnosis` | object \| null |  | Structured diagnosis behind the selected action when available. |
| `expected_lift` | object \| null |  | Directional expected-lift estimate and assumptions when available. |
| `validation` | Validation \| null |  | Metrics, success conditions, timing, and comparison used to validate the action. |
| `validation_result` | ValidationResult \| null |  | Result after validation has observed the completed action. |
| `evidence_summary` | object \| null |  | Bounded response, citation, and source evidence for the action. Each top\_sources row may include supports: up to 3 competitors that source backs in AI answers. |
| `fixes` | Fix\[\] \| null |  | Machine-readable corrections when the action comes from AI answers stating a business fact wrong: the stated value, the correct value, its source, and the third-party pages found carrying the wrong value. Null for every other action. |
| `guide` | Guide \| null |  | Structured implementation guide when available. |
| `topics` | TopicReference\[\] |  | Topics connected through supporting prompts and findings. |
| `started_at` | datetime \| null |  | When the action first entered in\_progress. |
| `completed_at` | datetime \| null |  | When the action was completed. |
| `measured_outcome` | MeasuredOutcome \| null |  | Targeted-prompt presence before and after completion. Present only when status is completed. Pending until the next visibility run can be compared. |
| `dismissed_at` | datetime \| null |  | When the action was dismissed. |
| `resolved_at` | datetime \| null |  | When supporting findings were automatically resolved. |
| `created_at` | datetime |  | Action creation time. |
| `updated_at` | datetime |  | Last action update time. |

#### StructuredTarget

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `label` | string \| null |  | Target display label. |
| `platform` | string \| null |  | Target platform. |
| `url` | URL \| null |  | Target URL. |
| `state` | existing\_page \| proposed\_page \| external\_destination \| scope \| null |  | How the target should be handled. |

#### ActionDependency

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `action_id` | uuid |  | Required action identifier. |
| `title` | string |  | Required action title. |
| `reason` | string \| null |  | Why it should be completed first. |

#### ExternalProfile

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | found \| readable \| unavailable \| not\_found \| unknown |  | Availability of the external profile. |
| `url` | URL \| null |  | External-profile URL. |

#### Validation

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `observation_window_days` | integer \| null |  | Days allowed to observe the expected result. |
| `comparison` | string \| null |  | Plain-language description of how the result is evaluated. |

#### ValidationResult

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | success \| failure \| inconclusive \| not\_observable |  | Outcome of the latest validation observation. |
| `reason` | string \| null |  | Explanation when validation did not succeed. |
| `observed_at` | datetime \| null |  | When the result was last observed. |
| `window_ends_at` | datetime \| null |  | When the observation window ends. |

#### MeasuredOutcome

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | string |  | pending or measured. |
| `reason` | string \| null |  | Present when pending: awaiting\_post\_completion\_run, no\_baseline\_run, no\_targeted\_prompts, or missing\_targeted\_executions. |
| `outcome` | string \| null |  | Present when measured: improved, unchanged, or declined. |
| `targeted` | object \| null |  | Presence on this action’s validation prompts: before, after, and sample\_size. |
| `other` | object \| null |  | Presence on all other prompts in the same runs: before, after, and sample\_size. |
| `baseline_at` | datetime \| null |  | When the pre-completion visibility run finished. |
| `comparison_at` | datetime \| null |  | When the post-completion visibility run finished. |

#### Fix

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `field` | phone \| website \| address \| hours \| closed |  | The business fact AI answers get wrong. |
| `qualifier` | string \| null |  | Weekday or weekday range for hours (monday … sunday, monday-saturday). Null for other fields. |
| `wrong` | string |  | The value AI answers state, normalized: HH:MM-HH:MM or closed for hours; E.164 for phone; permanently, temporarily, or moved for closed. |
| `correct` | string \| null |  | The value on record, normalized the same way. |
| `correct_source` | website \| location\_record \| google\_business\_profile \| customer \| null |  | Where the correct value comes from. |
| `stated_by` | string\[\] |  | AI models whose answers state the wrong value. |
| `carried_by` | FixSource\[\] |  | Third-party pages the answers cite that were read and found stating the wrong value: domain, url, and a verbatim quote from the page. Empty when none was found; the fact then needs correcting on the business’s own surfaces. |

#### Guide

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `diagnosis` | string |  | Concise explanation of the diagnosed issue. |
| `gap_analysis` | string |  | Difference between the current and desired state. |
| `action_steps` | string\[\] |  | Ordered implementation steps. |
| `validation_steps` | string\[\] |  | Checks used to confirm completion. |
| `rollback_notes` | string \| null |  | Recovery guidance when a change must be reverted. |
| `impact_prediction` | string |  | Expected outcome after implementation. |
| `impact_timeline_days` | integer \| null |  | Estimated days before an outcome may become measurable. |
| `success_metrics` | SuccessMetric\[\] \| null |  | How success is measured for this action. Null when no measurable metric applies. |
| `execution_artifact` | ExecutionArtifact \| null |  | Ready-to-use copy the action steps refer to: page text, brief, message or checklist. |

#### SuccessMetric

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `metric` | string |  | What is measured. |
| `baseline` | string |  | Value before the action: plain words, or mentions over sampled AI answers such as "1/2". |
| `target_direction` | increase \| decrease \| maintain \| reach |  | Expected direction of the metric. |
| `evaluation_window_days` | integer |  | Days after completion before the metric is evaluated. |
| `verification_method` | string |  | How the result is checked. |

#### ExecutionArtifact

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `type` | content\_brief \| outreach\_template \| implementation\_checklist \| schema\_snippet |  | Kind of copy. |
| `title` | string |  | Short title of the copy. |
| `content` | string |  | The copy as plain text or Markdown. Bracketed placeholders such as \[opening hours\] are for the business to fill in. |
| `language` | string \| null |  | ISO 639-1 code when the copy is in a different language from the guide. |
| `email` | { to?: string, subject?: string, body: string } \| null |  | outreach\_template only: the pitch as an email. to is an address or a role such as "the editor". content holds the email body. |
| `sections` | { heading: string, content: string }\[\] \| null |  | content\_brief only: the page copy in parts (title tag, intro, FAQ…), each ready to paste. heading may be empty for an untitled intro. content holds the same copy as text. |
| `fields` | { name: string, value: string }\[\] \| null |  | Listing actions: the profile fields to set (description, category, hours…) and the value for each. content holds the same fields as text. |

#### TopicReference

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Topic identifier. |
| `name` | string |  | Topic display name. |

#### Finding

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `source` | string |  | Analysis source that produced the finding. |
| `category` | string |  | Finding category. |
| `severity` | string |  | Finding severity. |
| `status` | string |  | Current finding lifecycle status. |
| `target_kind` | string |  | Type of resource that produced the signal. |
| `target` | string \| null |  | Target value when available. |
| `title` | string |  | Finding title. |
| `description` | string \| null |  | Evidence-backed finding description. |
| `recommendation` | string \| null |  | Recommended response to the finding. |
| `first_seen_at` | datetime |  | First detection time. |
| `last_seen_at` | datetime |  | Most recent detection time. |
| `resolved_at` | datetime \| null |  | Resolution time, when resolved. |

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/locations/{location_id}/actions/{action_id}' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "location_id": "a1308d14-149c-4dd7-a4c5-295ac9090f58",
  "action": {
    "id": "8ec60fe5-9c0b-41ea-98ce-9c98f846466f",
    "status": "in_progress",
    "priority": "high",
    "estimated_impact": 64,
    "effort_level": "low",
    "feasibility": "high",
    "source_category": "general",
    "content_format": null,
    "action_type": "directory_listing",
    "title": "Correct the Sunday opening hours on CityList",
    "description": "ChatGPT and Gemini tell customers the branch opens 09:00-19:00 on Sundays; the Google Business Profile states 10:00-18:00. The CityList page both assistants cite carries the wrong hours.",
    "recommendation": "Update the CityList listing to 10:00-18:00 on Sundays and confirm the hours match the Google Business Profile.",
    "target_kind": "domain",
    "target": "https://citylist.example/acme-phoenix",
    "structured_target": {
      "label": "CityList",
      "platform": "citylist.example",
      "url": "https://citylist.example/acme-phoenix",
      "state": "external_destination"
    },
    "external_profile": {
      "status": "readable",
      "url": "https://citylist.example/acme-phoenix"
    },
    "guide": {
      "diagnosis": "ChatGPT and Gemini cite the CityList page for Sunday hours, and it still lists 09:00-19:00.",
      "gap_analysis": "The Google Business Profile states 10:00-18:00 on Sundays; CityList is the only cited source with different hours.",
      "action_steps": [
        "Sign in to the CityList business dashboard for the Phoenix branch.",
        "Set the Sunday hours to 10:00-18:00 and save.",
        "Check that the public listing page shows the new hours."
      ],
      "validation_steps": [
        "Open https://citylist.example/acme-phoenix and confirm Sunday shows 10:00-18:00."
      ],
      "rollback_notes": null,
      "impact_prediction": "AI answers that cite CityList state the correct Sunday hours.",
      "impact_timeline_days": 21,
      "success_metrics": [
        {
          "metric": "AI answers stating the correct Sunday hours",
          "baseline": "ChatGPT 09:00-19:00, Gemini 09:00-19:00",
          "target_direction": "reach",
          "evaluation_window_days": 21,
          "verification_method": "Re-run the tracked prompts and compare the stated hours with the Google Business Profile."
        }
      ],
      "execution_artifact": {
        "type": "implementation_checklist",
        "title": "CityList hours update",
        "content": "Sunday: 10:00-18:00",
        "language": null,
        "email": null,
        "sections": null,
        "fields": [
          { "name": "Sunday hours", "value": "10:00-18:00" }
        ]
      }
    },
    "fixes": [
      {
        "field": "hours",
        "qualifier": "sunday",
        "wrong": "09:00-19:00",
        "correct": "10:00-18:00",
        "correct_source": "google_business_profile",
        "stated_by": ["chatgpt", "gemini"],
        "carried_by": [
          {
            "domain": "citylist.example",
            "url": "https://citylist.example/acme-phoenix",
            "quote": "Sunday 9:00 - 19:00"
          }
        ]
      }
    ],
    "topics": [],
    "started_at": "2026-07-30T10:05:00Z",
    "completed_at": null,
    "measured_outcome": null,
    "dismissed_at": null,
    "resolved_at": null,
    "created_at": "2026-07-20T08:30:00Z",
    "updated_at": "2026-07-30T10:05:00Z"
  },
  "findings": [
    {
      "source": "signals",
      "category": "fact_drift",
      "severity": "medium",
      "status": "open",
      "target_kind": "page",
      "target": "https://example.com/phoenix",
      "title": "AI answers state different opening hours",
      "description": "chatgpt and gemini give 09:00-19:00 for Sunday; the Google Business Profile states 10:00-18:00.",
      "recommendation": "Publish the correct Sunday hours (10:00-18:00) consistently on the website, the Google Business Profile and the listings AI assistants read, and correct the sources that carry 09:00-19:00.",
      "first_seen_at": "2026-07-10T07:30:00Z",
      "last_seen_at": "2026-07-30T09:30:00Z",
      "resolved_at": null
    }
  ]
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "details": { "status": ["is not supported"] },
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request |  | A path value, query parameter, or JSON body is malformed. |
| `401` | invalid\_api\_key |  | The Bearer API key is absent or invalid. |
| `403` | forbidden |  | The API key cannot perform this operation. |
| `404` | not\_found |  | Project, location, or action was not found. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Update action status

`PATCH /projects/{project_id}/locations/{location_id}/actions/{action_id}`

Updates the customer-managed lifecycle status of an action. Resolved actions are controlled automatically by their supporting findings.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `location_id` | location UUID \| location external ID | Required | Location identifier. |
| `action_id` | uuid | Required | Action identifier. |

#### Request body

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | todo \| in\_progress \| completed \| dismissed |  | New customer-managed action status. |

#### Allowed transitions

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `todo` | → in\_progress, completed, dismissed |  | A new action can be started, completed, or dismissed. |
| `in_progress` | → todo, completed, dismissed |  | Work can be paused, completed, or dismissed. |
| `completed` | → todo |  | A completed action can be reopened. |
| `dismissed` | → todo |  | A dismissed action can be reopened. |

#### Action response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `location_id` | uuid |  | Resolved Ceyo location identifier. |
| `action` | Action |  | Updated action. |

#### Action

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Unique action identifier. |
| `status` | string |  | todo, in\_progress, completed, dismissed, or resolved. |
| `priority` | string |  | low, medium, high, or critical. |
| `expected_outcome` | object |  | How the impact was estimated. \`state\` is critical\_rule, modelled, directional, or unknown. \`score\` (0-100), \`range\`, and \`unit\` are only present when state is modelled; \`assumption\` explains why. Display "unknown" rather than a number for unknown and directional outcomes. |
| `estimated_impact` | integer |  | Deprecated: relative ranking score from 0 to 100, not a forecast. Show expected\_outcome to customers; this value is present even when no outcome can be modelled. |
| `effort_level` | low \| medium \| high |  | Estimated implementation effort. |
| `feasibility` | low \| medium \| high |  | Estimated ability to complete the recommendation. |
| `source_category` | string |  | owned, earned, reputation, local, competitive, operational, or general. |
| `content_format` | string \| null |  | Recommended deliverable format when relevant. |
| `action_type` | string |  | technical, content, visibility, competitor, listing, earned\_editorial, earned\_ugc, earned\_reference, sentiment\_correction, general, access, measurement\_fix, entity, facts\_page, claim\_parity, content\_extend, local\_template, reputation\_programme, marketplace\_listing, directory\_listing, list\_inclusion, partner\_integration, community, or site\_health. |
| `title` | string |  | Short action title. |
| `description` | string \| null |  | Problem or opportunity addressed by the action. |
| `recommendation` | string \| null |  | Recommended outcome or approach. |
| `target_kind` | string |  | Type of resource targeted by the action. |
| `target` | string \| null |  | Target URL, prompt reference, profile, or scope value. |
| `structured_target` | StructuredTarget \| null |  | Execution-ready target label, platform, URL, and state. |
| `dependencies` | ActionDependency\[\] |  | Actions that should be completed first. |
| `external_profile` | ExternalProfile \| null |  | URL and availability status for an external profile. |
| `diagnosis` | object \| null |  | Structured diagnosis behind the selected action when available. |
| `expected_lift` | object \| null |  | Directional expected-lift estimate and assumptions when available. |
| `validation` | Validation \| null |  | Metrics, success conditions, timing, and comparison used to validate the action. |
| `validation_result` | ValidationResult \| null |  | Result after validation has observed the completed action. |
| `evidence_summary` | object \| null |  | Bounded response, citation, and source evidence for the action. Each top\_sources row may include supports: up to 3 competitors that source backs in AI answers. |
| `fixes` | Fix\[\] \| null |  | Machine-readable corrections when the action comes from AI answers stating a business fact wrong: the stated value, the correct value, its source, and the third-party pages found carrying the wrong value. Null for every other action. |
| `guide` | Guide \| null |  | Structured implementation guide when available. |
| `topics` | TopicReference\[\] |  | Topics connected through supporting prompts and findings. |
| `started_at` | datetime \| null |  | When the action first entered in\_progress. |
| `completed_at` | datetime \| null |  | When the action was completed. |
| `measured_outcome` | MeasuredOutcome \| null |  | Targeted-prompt presence before and after completion. Present only when status is completed. Pending until the next visibility run can be compared. |
| `dismissed_at` | datetime \| null |  | When the action was dismissed. |
| `resolved_at` | datetime \| null |  | When supporting findings were automatically resolved. |
| `created_at` | datetime |  | Action creation time. |
| `updated_at` | datetime |  | Last action update time. |

#### StructuredTarget

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `label` | string \| null |  | Target display label. |
| `platform` | string \| null |  | Target platform. |
| `url` | URL \| null |  | Target URL. |
| `state` | existing\_page \| proposed\_page \| external\_destination \| scope \| null |  | How the target should be handled. |

#### ActionDependency

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `action_id` | uuid |  | Required action identifier. |
| `title` | string |  | Required action title. |
| `reason` | string \| null |  | Why it should be completed first. |

#### ExternalProfile

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | found \| readable \| unavailable \| not\_found \| unknown |  | Availability of the external profile. |
| `url` | URL \| null |  | External-profile URL. |

#### Validation

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `observation_window_days` | integer \| null |  | Days allowed to observe the expected result. |
| `comparison` | string \| null |  | Plain-language description of how the result is evaluated. |

#### ValidationResult

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | success \| failure \| inconclusive \| not\_observable |  | Outcome of the latest validation observation. |
| `reason` | string \| null |  | Explanation when validation did not succeed. |
| `observed_at` | datetime \| null |  | When the result was last observed. |
| `window_ends_at` | datetime \| null |  | When the observation window ends. |

#### MeasuredOutcome

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `status` | string |  | pending or measured. |
| `reason` | string \| null |  | Present when pending: awaiting\_post\_completion\_run, no\_baseline\_run, no\_targeted\_prompts, or missing\_targeted\_executions. |
| `outcome` | string \| null |  | Present when measured: improved, unchanged, or declined. |
| `targeted` | object \| null |  | Presence on this action’s validation prompts: before, after, and sample\_size. |
| `other` | object \| null |  | Presence on all other prompts in the same runs: before, after, and sample\_size. |
| `baseline_at` | datetime \| null |  | When the pre-completion visibility run finished. |
| `comparison_at` | datetime \| null |  | When the post-completion visibility run finished. |

#### Fix

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `field` | phone \| website \| address \| hours \| closed |  | The business fact AI answers get wrong. |
| `qualifier` | string \| null |  | Weekday or weekday range for hours (monday … sunday, monday-saturday). Null for other fields. |
| `wrong` | string |  | The value AI answers state, normalized: HH:MM-HH:MM or closed for hours; E.164 for phone; permanently, temporarily, or moved for closed. |
| `correct` | string \| null |  | The value on record, normalized the same way. |
| `correct_source` | website \| location\_record \| google\_business\_profile \| customer \| null |  | Where the correct value comes from. |
| `stated_by` | string\[\] |  | AI models whose answers state the wrong value. |
| `carried_by` | FixSource\[\] |  | Third-party pages the answers cite that were read and found stating the wrong value: domain, url, and a verbatim quote from the page. Empty when none was found; the fact then needs correcting on the business’s own surfaces. |

#### Guide

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `diagnosis` | string |  | Concise explanation of the diagnosed issue. |
| `gap_analysis` | string |  | Difference between the current and desired state. |
| `action_steps` | string\[\] |  | Ordered implementation steps. |
| `validation_steps` | string\[\] |  | Checks used to confirm completion. |
| `rollback_notes` | string \| null |  | Recovery guidance when a change must be reverted. |
| `impact_prediction` | string |  | Expected outcome after implementation. |
| `impact_timeline_days` | integer \| null |  | Estimated days before an outcome may become measurable. |
| `success_metrics` | SuccessMetric\[\] \| null |  | How success is measured for this action. Null when no measurable metric applies. |
| `execution_artifact` | ExecutionArtifact \| null |  | Ready-to-use copy the action steps refer to: page text, brief, message or checklist. |

#### SuccessMetric

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `metric` | string |  | What is measured. |
| `baseline` | string |  | Value before the action: plain words, or mentions over sampled AI answers such as "1/2". |
| `target_direction` | increase \| decrease \| maintain \| reach |  | Expected direction of the metric. |
| `evaluation_window_days` | integer |  | Days after completion before the metric is evaluated. |
| `verification_method` | string |  | How the result is checked. |

#### ExecutionArtifact

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `type` | content\_brief \| outreach\_template \| implementation\_checklist \| schema\_snippet |  | Kind of copy. |
| `title` | string |  | Short title of the copy. |
| `content` | string |  | The copy as plain text or Markdown. Bracketed placeholders such as \[opening hours\] are for the business to fill in. |
| `language` | string \| null |  | ISO 639-1 code when the copy is in a different language from the guide. |
| `email` | { to?: string, subject?: string, body: string } \| null |  | outreach\_template only: the pitch as an email. to is an address or a role such as "the editor". content holds the email body. |
| `sections` | { heading: string, content: string }\[\] \| null |  | content\_brief only: the page copy in parts (title tag, intro, FAQ…), each ready to paste. heading may be empty for an untitled intro. content holds the same copy as text. |
| `fields` | { name: string, value: string }\[\] \| null |  | Listing actions: the profile fields to set (description, category, hours…) and the value for each. content holds the same fields as text. |

#### TopicReference

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Topic identifier. |
| `name` | string |  | Topic display name. |

#### Request and response

```curl
curl --request PATCH \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/locations/{location_id}/actions/{action_id}' \
  --header 'Authorization: Bearer ceyo_platform_...' \
  --header 'Content-Type: application/json' \
  --data '{"status":"completed"}'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "location_id": "a1308d14-149c-4dd7-a4c5-295ac9090f58",
  "action": {
    "id": "8ec60fe5-9c0b-41ea-98ce-9c98f846466f",
    "status": "completed",
    "completed_at": "2026-07-31T10:15:00Z",
    "measured_outcome": {
      "status": "pending",
      "reason": "awaiting_post_completion_run"
    },
    "updated_at": "2026-07-31T10:15:00Z"
  }
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "details": { "status": ["is not supported"] },
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request |  | A path value, query parameter, or JSON body is malformed. |
| `401` | invalid\_api\_key |  | The Bearer API key is absent or invalid. |
| `403` | forbidden |  | The API key cannot perform this operation. |
| `404` | not\_found |  | Project, location, or action was not found. |
| `409` | invalid\_transition |  | The requested status transition is not allowed. |
| `422` | invalid\_status |  | The submitted status is not writable. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |

### Get action impact

`GET /projects/{project_id}/locations/{location_id}/actions/impact`

Compares visibility signals before and after completed actions in the selected location. Actions remain pending until both baseline and comparison runs are available.

#### Path parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | project UUID \| project external ID | Required | Project identifier. |
| `location_id` | location UUID \| location external ID | Required | Location identifier. |

#### Query parameters

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `start_on` | date | Optional; Default: 90 days before end\_on | First completion date to include. |
| `end_on` | date | Optional; Default: Today | Last completion date to include. |
| `topic_id` | uuid | Optional | Measure actions connected to one topic. |
| `action_type` | string | Optional | Measure one action type. |

#### Response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `project_id` | uuid |  | Resolved Ceyo project identifier. |
| `location_id` | uuid |  | Resolved Ceyo location identifier. |
| `period` | ImpactPeriod |  | Resolved start\_on and end\_on. |
| `summary` | ImpactSummary |  | Completed, measured, pending, and outcome totals. |
| `timeline` | ImpactTimelinePoint\[\] |  | Weekly completion and visibility outcome series. |
| `actions` | ActionMeasurement\[\] |  | Measurements for up to 100 completed actions. |

#### ImpactPeriod

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `start_on` | date |  | First completion date included in the measurement window. |
| `end_on` | date |  | Last completion date included in the measurement window. |

#### ImpactSummary

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `completed_actions` | integer |  | Completed actions included in the measurement window. |
| `measured_actions` | integer |  | Actions with both baseline and comparison signals. |
| `pending_actions` | integer |  | Actions awaiting an eligible baseline or comparison signal. |
| `improved` | integer |  | Measured actions classified as improved. |
| `unchanged` | integer |  | Measured actions classified as unchanged. |
| `declined` | integer |  | Measured actions classified as declined. |
| `average_visibility_delta` | number \| null |  | Mean visibility percentage-point change, or null when no action is measured. |
| `average_citation_delta` | number \| null |  | Mean citation-count change, or null when no action is measured. |
| `average_sentiment_delta` | number \| null |  | Mean sentiment-score change, or null when no comparable values exist. |

#### ImpactTimelinePoint

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `date` | date |  | Start date of the represented week. |
| `completed` | integer |  | Actions completed during the represented week. |
| `measured` | integer |  | Completed actions with an available measurement. |
| `average_visibility_delta` | number \| null |  | Mean visibility percentage-point change, or null when the week has no measurements. |

#### ActionMeasurement

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Action identifier. |
| `title` | string |  | Action title. |
| `action_type` | string |  | Action type. |
| `completed_at` | datetime |  | Action completion time. |
| `topics` | TopicReference\[\] |  | Topics used to scope the measurement. |
| `status` | measured \| pending |  | Measurement availability. |
| `reason` | no\_baseline\_run \| awaiting\_post\_completion\_run \| null |  | Why a pending action cannot yet be measured. |
| `outcome` | improved \| unchanged \| declined \| null |  | Outcome derived from visibility change when measured. |
| `baseline_at` | datetime \| null |  | Baseline run completion time. |
| `comparison_at` | datetime \| null |  | Comparison run completion time. |
| `baseline` | ImpactSnapshot \| null |  | Signals immediately before completion when measured. |
| `comparison` | ImpactSnapshot \| null |  | Latest eligible signals after completion when measured. |
| `delta` | ActionMeasurementDelta \| null |  | Signal changes when the action is measured. |

#### ActionMeasurementDelta

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `visibility_percentage_points` | number \| null |  | Comparison visibility minus baseline visibility. |
| `citation_count` | integer \| null |  | Comparison citation count minus baseline citation count. |
| `sentiment_score` | number \| null |  | Comparison sentiment score minus baseline sentiment score. |
| `average_position` | number \| null |  | Improvement in average position; positive values indicate movement toward position one. |

#### ImpactSnapshot

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `visibility_percentage` | number |  | Primary brand visibility percentage. |
| `citation_count` | integer |  | Distinct cited pages. |
| `sentiment_score` | number \| null |  | Average primary-brand sentiment score. |
| `average_position` | number \| null |  | Average primary-brand position. |

#### TopicReference

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `id` | uuid |  | Topic identifier. |
| `name` | string |  | Topic display name. |

> **Outcome interpretation**
>
> Measurements show correlated before-and-after signals. They do not establish that an action was the only cause of a change.

#### Request and response

```curl
curl --request GET \
  --url 'https://api.signal.ceyo.ai/v1/projects/{project_id}/locations/{location_id}/actions/impact?start_on=2026-05-01&end_on=2026-07-30' \
  --header 'Authorization: Bearer ceyo_platform_...'
```

```json
{
  "project_id": "e6c96c98-d777-40e0-94ec-48931f57782f",
  "location_id": "a1308d14-149c-4dd7-a4c5-295ac9090f58",
  "period": {
    "start_on": "2026-05-01",
    "end_on": "2026-07-30"
  },
  "summary": {
    "completed_actions": 12,
    "measured_actions": 9,
    "pending_actions": 3,
    "improved": 6,
    "unchanged": 2,
    "declined": 1,
    "average_visibility_delta": 4.8,
    "average_citation_delta": 3.2,
    "average_sentiment_delta": 0.4
  },
  "timeline": [
    {
      "date": "2026-07-27",
      "completed": 3,
      "measured": 2,
      "average_visibility_delta": 5.1
    }
  ],
  "actions": [
    {
      "id": "8ec60fe5-9c0b-41ea-98ce-9c98f846466f",
      "title": "Create a focused comparison page",
      "action_type": "content",
      "completed_at": "2026-07-10T10:15:00Z",
      "topics": [],
      "status": "measured",
      "outcome": "improved",
      "baseline_at": "2026-07-09T07:30:00Z",
      "comparison_at": "2026-07-30T09:30:00Z",
      "baseline": {
        "visibility_percentage": 35.0,
        "citation_count": 8,
        "sentiment_score": 6.8,
        "average_position": 3.4
      },
      "comparison": {
        "visibility_percentage": 42.5,
        "citation_count": 13,
        "sentiment_score": 7.3,
        "average_position": 2.8
      },
      "delta": {
        "visibility_percentage_points": 7.5,
        "citation_count": 5,
        "sentiment_score": 0.5,
        "average_position": 0.6
      }
    }
  ]
}
```

#### Errors

#### Error response envelope

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `error` | Error |  | Structured error payload. |

#### Error

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `code` | string |  | Stable snake\_case code suitable for programmatic handling. |
| `message` | string |  | Human-readable explanation of the failure. |
| `details` | object \| array \| null |  | Structured validation or request context when available. |
| `request_id` | string |  | Identifier to provide when requesting support. |

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request parameters are invalid.",
    "details": { "status": ["is not supported"] },
    "request_id": "req_01K1JQY1RQQ7N3C5H1K6J0P8AT"
  }
}
```

#### Status codes

| Name | Type | Details | Description |
| --- | --- | --- | --- |
| `400` | invalid\_request |  | A path value, query parameter, or JSON body is malformed. |
| `401` | invalid\_api\_key |  | The Bearer API key is absent or invalid. |
| `403` | forbidden |  | The API key cannot perform this operation. |
| `404` | not\_found |  | Project, location, or action was not found. |
| `429` | rate\_limit\_exceeded |  | Too many requests were made. |
