---
title: "Create Landing Page Agent Session"
method: POST
path: "/landing-pages/{page_id}/agent-sessions"
tags: ["Landing Pages"]
---

# Create Landing Page Agent Session

`POST /landing-pages/{page_id}/agent-sessions`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/landing-pages/{page_id}/agent-sessions`
- Auth: HTTP bearer

Open a short-lived edit session against one landing page.

Returns an `edit_session` id to pass to the two routes above. The session is
a LOCATOR, not a credential: it selects which draft the following calls act
on, and `get_api_user` still authenticates every one of them. It carries a
TTL and is bound at creation to this org and this actor.

`actor_email` is `str(user.id)`, byte-identical to what the read and patch
routes pass to `require_edit_session`. That is what closes the loop: a
session minted here resolves for the same key that minted it, and a mismatch
would make every subsequent call 404 against a session that plainly exists.

NO OWNABILITY GUARD, unlike the agent routes. `agent_router` refuses a write
from a key whose `actor_identities()` is empty, because an org key
authenticates as the shared constant `api-key@graph8.com`. The same constant
lands in `actor_email` here, so two org keys in ONE org can read and patch
each other's sessions. That is deliberate parity rather than an oversight:
the two sibling routes already bind on `str(user.id)` and already accept org
keys, so guarding only the create half would break the loop for exactly the
keys the other half still serves. The boundary that matters is unchanged --
`require_edit_session` rejects a session from another ORG. Tightening all
three together belongs with the per-key identity work recorded in
`.claude/agent-context/agent-runtime-api-key-door.md`.

NOT SCOPE-GATED LOCALLY, unlike its two siblings, and that is deliberate
rather than an oversight. They call `require_scope("landing-pages:...")`,
which makes `build_scope_policy` record them as `exempt`; an exempt route
publishes no `x-graph8-scope`, so their requirement is real but invisible in
the contract -- and `landing-pages` is not a resource in
`scope_vocabulary.json` at all, so the scope they name cannot be granted to
a key. It is inert today only because `require_scope` runs in soak mode by
default. Leaving this route ungated puts it in the GOVERNED set, where
`apply_scope_policy` attaches the gate and the bundle publishes
`integrations:write` -- the resource this file is actually mapped to. The
siblings are left alone here: correcting them moves two published operations
and belongs in its own change.

## Path parameters

- `page_id` string, required

## Headers

- `Authorization` string, nullable
- `X-Target-Org-Id` string, nullable

## Request body

- CreateAgentSessionRequest: How the agent wants its edits handled once it starts patching.
  - `approval_mode` string: 'review' (default) stages each patch for a human to approve in Studio. 'auto_apply' writes straight to the draft. Bound to the session at creation, so a later patch cannot escalate itself.

## Response `201`

Successful Response

- ApiResponseDictStrAny
  - `data` object, required
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
