# Artifacts (`artifact`)

A document to read: a plan, summary, draft, notes or instructions written in markdown. Display only: the page asks for no answer.

**When to use it:** You give the person a document to read: a plan, a summary, a draft, notes or instructions, written in markdown. It only displays information; the person does not answer.

**When not to use it:** Do not use it for a short reply you can write in chat, for numbers that belong in a `chart` or a `table`, or to ask the person something: use `form` or a picker.

**Gather first:**

- The finished text. The page shows exactly what you send, so write it in full first.

**Writing good content:**

- Put the takeaway in `title` and start the document with what matters most; the page title is already the main heading, so use `##` for sections.
- Prefer short sections, lists and small tables over long paragraphs: the person reads it on a phone.
- Links must start with https:// or mailto:. There are no images and no HTML.
- It is display-only: if the person should reply, ask in chat after you send the link.

**Display-only:** the page shows information and has no Submit button. It accepts no answer, so no result ever comes back.

## Fields

Every page also has `version` (always `1`), `title`, and optionally `description`, `branding`, `cover` (a header photo) and `expiresAt`; see [Page fields](/docs/api#page-fields). These are the fields of `page` (the page type and its data):

| Field | Description |
| --- | --- |
| `markdown` | *string, required.* The document, in markdown (GitHub-flavored), up to 20,000 characters: headings, paragraphs, lists, tables, code blocks, quotes, bold, emphasis and links. Only https:// and mailto: links work; any other link shows as its text. No images, and HTML shows as plain text. Limits: 1-20000 characters. |

**Why these are required:** The document itself, in GitHub-flavored markdown (headings, lists, tables, code, quotes, bold, emphasis and links; only https and mailto links are followed; no images, and HTML shows as text). Display-only: no answer.

**When optional fields are left out:** Only `markdown`, up to 20,000 characters. Headings nest under the page title (the top level becomes a section heading); a link that is not https or mailto shows as its text. The link preview lists its section headings, or its first line of text.

## Minimal example

Only the required fields:

```json
{
  "version": 1,
  "title": "Packing list",
  "page": {
    "type": "artifact",
    "markdown": "- Passport\n- Charger\n- Rain jacket"
  }
}
```

## Full example

```json
{
  "version": 1,
  "title": "Your Lisbon weekend plan",
  "description": "Draft from our chat. Tell me in chat what to change.",
  "branding": {
    "name": "Wayfarer",
    "accentColor": "#1f5f8b"
  },
  "page": {
    "type": "artifact",
    "markdown": "## Friday\n\nLand at **10:40**, then take the metro (red line) to *Saldanha* and change to the yellow line for the hotel.\n\n1. Check in at Casa do Largo from 15:00\n2. Sunset at Miradouro da Senhora do Monte\n3. Dinner at 20:30 near Alfama\n\n## Saturday\n\n| Time | Plan | Cost |\n|---|---|---|\n| 09:30 | Tram 28 to Graça | €3 |\n| 11:00 | Tile museum | €8 |\n| 14:00 | Belém and pastéis | €12 |\n\n> Tram 28 fills up after 10:00. Board at Martim Moniz to get a seat.\n\n## Booking references\n\nHotel confirmation: `CDL-48213`. Full details on the [hotel site](https://example.com/casa-do-largo)."
  }
}
```

## Send it

### eve, AI SDK and MCP

With [`@pagesystems/eve`](/docs#quickstart-eve), [`@pagesystems/ai-sdk`](/docs#quickstart-ai-sdk) or the [MCP server](/docs#quickstart-mcp), the model calls `get_page_type_schema` with `"artifact"` (the same JSON as [`GET /v1/page-types/artifact`](/docs/api#endpoints)), then `create_page` with this input (no `version` or `branding`; branding comes from your dashboard):

```json
{
  "title": "Your Lisbon weekend plan",
  "description": "Draft from our chat. Tell me in chat what to change.",
  "page": {
    "type": "artifact",
    "markdown": "## Friday\n\nLand at **10:40**, then take the metro (red line) to *Saldanha* and change to the yellow line for the hotel.\n\n1. Check in at Casa do Largo from 15:00\n2. Sunset at Miradouro da Senhora do Monte\n3. Dinner at 20:30 near Alfama\n\n## Saturday\n\n| Time | Plan | Cost |\n|---|---|---|\n| 09:30 | Tram 28 to Graça | €3 |\n| 11:00 | Tile museum | €8 |\n| 14:00 | Belém and pastéis | €12 |\n\n> Tram 28 fills up after 10:00. Board at Martim Moniz to get a seat.\n\n## Booking references\n\nHotel confirmation: `CDL-48213`. Full details on the [hotel site](https://example.com/casa-do-largo)."
  }
}
```

### REST

```bash
curl https://pagesystems.ai/v1/pages \
  -H "Authorization: Bearer $PAGE_SYSTEMS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"spec":{"version":1,"title":"Your Lisbon weekend plan","description":"Draft from our chat. Tell me in chat what to change.","branding":{"name":"Wayfarer","accentColor":"#1f5f8b"},"page":{"type":"artifact","markdown":"## Friday\n\nLand at **10:40**, then take the metro (red line) to *Saldanha* and change to the yellow line for the hotel.\n\n1. Check in at Casa do Largo from 15:00\n2. Sunset at Miradouro da Senhora do Monte\n3. Dinner at 20:30 near Alfama\n\n## Saturday\n\n| Time | Plan | Cost |\n|---|---|---|\n| 09:30 | Tram 28 to Graça | €3 |\n| 11:00 | Tile museum | €8 |\n| 14:00 | Belém and pastéis | €12 |\n\n> Tram 28 fills up after 10:00. Board at Martim Moniz to get a seat.\n\n## Booking references\n\nHotel confirmation: `CDL-48213`. Full details on the [hotel site](https://example.com/casa-do-largo)."}}}'
```

## Validation errors

A spec that does not validate is refused with `400 invalid_spec`. The message names each problem and its path, so a model can fix the spec and try again. For example:

**Leaving out the required field `markdown`:**

```text
✖ Invalid input: expected string, received undefined
  → at page.markdown
```

**Misspelling a field name (unknown keys are rejected):**

```text
✖ Unrecognized key: "colour"
  → at page
```
