# Default tabs

A `default`-category tab is a list of independent records — tickets, invoices, tasks, alerts, anything where the rows don't relate to each other as a thread. Clicking a row opens a generic key-value detail screen with optional Update / Delete action buttons.

Pick `default` for flat lists. Pick [`messaging`](/oaura/tabs/messaging.md) for conversation threads (unread tracking, realtime updates, reply composer).

## Actions

| Action                                         | HTTP   | Routing                       | What it does                                            |
| ---------------------------------------------- | ------ | ----------------------------- | ------------------------------------------------------- |
| [`get_items`](#get_items--fetch-the-list)      | `GET`  | Server-proxied (`tab-fetch`)  | Fetch the list. Required.                               |
| [`item_clicked`](#item_clicked--open-one-item) | `GET`  | Server-proxied (`tab-action`) | Fetch the enriched detail when the visitor opens a row. |
| [`update_item`](#update_item--modify-an-item)  | `POST` | Direct from browser           | Per-row "Update" menu, detail-screen "Update" button.   |
| [`delete_item`](#delete_item--remove-an-item)  | `POST` | Direct from browser           | Per-row "Delete" menu, detail-screen "Delete" button.   |

`create_item` is not surfaced on default tabs — the widget has no built-in UI to fire it (no "new" button, no form). Wiring it up has no effect. For sending new items, use `create_item` on a [messaging tab](/oaura/tabs/messaging.md#create_item--send-a-reply) where the composer fires it.

For the general definitions of these actions and what "server-proxied" vs "direct" means, see [Prebuilt actions](/oaura/tabs/prebuilt-actions.md) and [Request format](/oaura/tabs/request-format.md).

***

## `get_items` — fetch the list

The widget fires this when the visitor opens the tab, gains focus, or refreshes.

### Request

`GET <your-url>?…` with the standard server-proxied query parameters:

| Param                                      | Notes                           |
| ------------------------------------------ | ------------------------------- |
| `action`                                   | `get_items`                     |
| `agentId`, `agentName`, `tabId`, `tabName` | Caller context                  |
| `sessionId`                                | Widget session                  |
| `endUserId`, `endUserEmail`                | Verified from the visitor's JWT |
| `_ts`                                      | Cache-buster                    |

No request body. See [Request format](/oaura/tabs/request-format.md) for the full reference.

### Response schema

Envelope on the outside, item shape on the inside, as one JSON Schema. The same `DefaultItem` shape is reused by `item_clicked` later — see [its section below](#item_clicked--open-one-item).

```json
{
  "type": "object",
  "required": ["items"],
  "additionalProperties": false,
  "properties": {
    "items": {
      "type": "array",
      "items": { "$ref": "#/$defs/DefaultItem" }
    },
    "badge": { "$ref": "#/$defs/Badge" }
  },
  "$defs": {
    "DefaultItem": {
      "type": "object",
      "required": ["id"],
      "additionalProperties": false,
      "properties": {
        "id":          { "type": "string" },
        "title":       { "type": "string" },
        "type":        { "type": "string" },
        "status":      { "type": "string" },
        "description": { "type": "string" },
        "timestamp":   { "type": "string", "format": "date-time" },
        "is_urgent":   { "type": "boolean" },
        "tags": {
          "type": "array",
          "items": { "$ref": "#/$defs/Tag" }
        }
      }
    },
    "Badge": {
      "type": "object",
      "required": ["color"],
      "properties": { "color": { "$ref": "#/$defs/PaletteColor" } }
    },
    "Tag": {
      "type": "object",
      "required": ["label", "color", "tooltip"],
      "additionalProperties": false,
      "properties": {
        "label":   { "type": "string" },
        "color":   { "$ref": "#/$defs/PaletteColor" },
        "tooltip": { "type": "string" }
      }
    },
    "PaletteColor": {
      "type": "string",
      "enum": ["green","yellow","red","blue","gray","purple","orange","teal","pink","brown"]
    }
  }
}
```

> **`view` is messaging-only.** It has no meaning on default tabs and is silently ignored if you include it.

Use these keys exactly. The widget does not accept aliases (no `name`/`subject` for `title`, no `preview`/`body` for `description`, no `createdAt` for `timestamp`). `DefaultItem` is `additionalProperties: false` — anything else is dropped.

The widget also accepts a bare array `[{…}, …]` as shorthand for `{ "items": [ … ] }` with no envelope. See [Response format](/oaura/tabs/response-format.md) for the full list of accepted wrapping shapes.

### Top-level fields

#### `items` — required

The list. Each element matches `DefaultItem`. Rendered in the exact order you return — the widget does not sort. Sort on your side, typically by `timestamp DESC`.

#### `badge` — optional

Colour of the unread badge on the tab strip. The count is `items.length`; you only supply the colour. Omit for no override (uses the agent accent).

### `DefaultItem` sub-schema

#### `id` — required

Unique within the list. Sent back as `id` to `item_clicked` / `update_item` / `delete_item`. UUIDs recommended.

#### `title`

Primary label on the row, heading on the detail screen. Missing → row falls back to `Item N`; detail screen falls back to the tab's own name.

#### `type`

Category label rendered as a small badge — e.g. `"Bug Report"`, `"Feature Request"`. Free-form string.

#### `status`

Current state rendered as a coloured badge. Free-form, but the widget recognises a small palette: `open` → blue, `pending` → yellow, `resolved` / `closed` / `completed` / `approved` → green, `declined` / `rejected` → red, `cancelled` → gray, `in_progress` → blue. Anything else → neutral gray.

#### `description`

Secondary line on the list row, body of the detail screen. **Rendered as Markdown** on the detail screen (GFM enabled — tables, code fences, links, lists). On the list row, used as a one-line preview, truncated.

Common pattern: return a brief summary in `get_items` and the full Markdown body in `item_clicked`.

#### `timestamp`

ISO 8601 date-time. Rendered as a relative time (`"2h ago"`, `"3d ago"`) until it crosses a week, then as a localised date.

#### `is_urgent`

Optional boolean. `true` adds an urgency indicator (alert icon, slight emphasis) on both list row and detail screen.

#### `tags` (sub-schema)

Coloured pill tags on the list row. Each tag is `{ label, color, tooltip }` — all three required.

| Field     | Notes                                                         |
| --------- | ------------------------------------------------------------- |
| `label`   | Short text shown inside the pill. Capitalised by the widget.  |
| `color`   | One of the `PaletteColor` values below.                       |
| `tooltip` | Shown on hover. Explain what the tag means in plain language. |

### `PaletteColor` enum

Shared between `Badge.color` and `Tag.color`:

```
green | yellow | red | blue | gray | purple | orange | teal | pink | brown
```

Tailwind has no `brown` — the widget renders it as amber/stone (reads as brown in both themes). Unknown colours fall back to gray.

### Examples

**Minimum response:**

```json
{ "items": [] }
```

**Typical response:**

```json
{
  "items": [
    {
      "id":          "t-42",
      "title":       "Cannot log in",
      "type":        "Bug Report",
      "status":      "open",
      "description": "Steps to reproduce:\n\n1. Go to /login\n2. Enter credentials\n3. Click submit\n\nExpected: redirect. Actual: blank page.",
      "timestamp":   "2026-05-18T10:00:00Z",
      "is_urgent":   true,
      "tags": [
        { "label": "regression", "color": "red", "tooltip": "Worked in the previous release" }
      ]
    },
    {
      "id":        "t-41",
      "title":     "Add SSO with Google Workspace",
      "type":      "Feature Request",
      "status":    "pending",
      "timestamp": "2026-05-17T14:22:00Z"
    }
  ],
  "badge": { "color": "blue" }
}
```

***

## `item_clicked` — open one item

Fires when the visitor clicks a list row. Without `item_clicked`, the row is non-clickable; the widget falls back to whatever data the row already contained from `get_items`.

### Request

`GET <your-url>?…` with the standard server-proxied query parameters plus:

| Param | Notes                                     |
| ----- | ----------------------------------------- |
| `id`  | The `DefaultItem.id` the visitor clicked. |

No request body.

### Response schema

A **single** `DefaultItem` — same `$defs/DefaultItem` schema as the items inside `get_items`. A single-element outer array `[{…}]` is auto-unwrapped, so n8n's default works.

```json
{
  "type": "object",
  "required": ["id"],
  "additionalProperties": false,
  "properties": {
    "id":          { "type": "string" },
    "title":       { "type": "string" },
    "type":        { "type": "string" },
    "status":      { "type": "string" },
    "description": { "type": "string" },
    "timestamp":   { "type": "string", "format": "date-time" },
    "is_urgent":   { "type": "boolean" },
    "tags": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["label", "color", "tooltip"],
        "additionalProperties": false,
        "properties": {
          "label":   { "type": "string" },
          "color":   { "type": "string", "enum": ["green","yellow","red","blue","gray","purple","orange","teal","pink","brown"] },
          "tooltip": { "type": "string" }
        }
      }
    }
  }
}
```

Field semantics are identical to those in `get_items` above. The only practical difference: `description` is rendered as Markdown on the detail screen (with GFM enabled — tables, code blocks, links, lists), while in the list row it's a one-line truncated preview. Return the brief preview in `get_items` and the full body here.

### Example

```json
{
  "id":          "t-42",
  "title":       "Cannot log in",
  "type":        "Bug Report",
  "status":      "open",
  "description": "## Steps to reproduce\n\n1. Go to /login\n2. Enter credentials\n3. Click submit\n\n## Expected\n\nRedirect to /dashboard.\n\n## Actual\n\nBlank page, console shows `TypeError: cannot read 'user' of null`.",
  "timestamp":   "2026-05-18T10:00:00Z",
  "is_urgent":   true,
  "tags": [
    { "label": "regression", "color": "red", "tooltip": "Worked in the previous release" }
  ]
}
```

***

## `update_item` — modify an item

Fired from the per-row "Update" menu item or the "Update" button on the detail screen. **Direct from the browser** — no edge function in between, no identity forwarding, no credential header. **CORS required.**

There is no form behind the button — the widget doesn't surface a UI to collect new field values. Whatever change you want to make has to be derivable from the click itself, the existing item, and `firedAt`. This makes the action useful for one-shot toggles (mute, pin, snooze) and useless for things that need user input.

### Request

`POST <your-url>`, body:

```json
{
  "action":   "update_item",
  "tabId":    "<uuid>",
  "tabName":  "<string>",
  "sessionId": "<widget session id>",
  "item":     { /* the full DefaultItem — list row from get_items or detail object from item_clicked */ },
  "firedAt":  "<iso timestamp>",
  "id":       "<picked from item.id — detail-screen path only>"
}
```

The full `item` is the row the visitor was looking at. Treat it as a hint; re-look up the canonical record by `item.id` in your store.

### Authorisation

No identity forwarding means you can't trust the call to be from any particular visitor. Mitigations:

* Look up `item.id` and verify ownership via a session cookie your visitor's browser carries (works only if your webhook is on the same eTLD+1 as your site), **or**
* Treat `get_items` as the authorisation gate — visitors wouldn't see this row in their list if they didn't own it, so re-deriving ownership server-side is sufficient.

### Response

Any JSON. The widget reads only the HTTP status. On 2xx it toasts "Done" and refetches the list. On non-2xx it toasts "Action failed".

### Example: mute a notification

```json
{
  "action":    "update_item",
  "tabId":     "01H...",
  "tabName":   "Notifications",
  "sessionId": "sess-abc",
  "item": {
    "id":        "n-7",
    "title":     "New comment",
    "type":      "comment",
    "status":    "unread",
    "timestamp": "2026-05-19T10:00:00Z"
  },
  "firedAt": "2026-05-20T09:14:00Z"
}
```

Webhook flips `muted = true` on row `n-7`, returns 204.

***

## `delete_item` — remove an item

Fires from the per-row "Delete" menu item (rendered in destructive red) or the detail-screen "Delete" button. **Direct from the browser**, same constraints as `update_item`. **No confirmation dialog** — the action fires immediately on click. The row disappears optimistically before your webhook responds.

### Request

`POST <your-url>`, body:

```json
{
  "action":   "delete_item",
  "tabId":    "<uuid>",
  "tabName":  "<string>",
  "sessionId": "<widget session id>",
  "item":     { /* the full DefaultItem */ },
  "firedAt":  "<iso timestamp>",
  "id":       "<picked from item.id — detail-screen path only>"
}
```

### Recommended handling

* **Soft delete preferred.** Set `deletedAt = now()` and filter `WHERE deletedAt IS NULL` in `get_items`. Reversible, audit-friendly.
* Re-verify ownership before deleting (same caveats as `update_item`).

### Response

Any JSON. 2xx → row disappears; non-2xx → row is restored and a toast surfaces the failure.

### Example

```json
{
  "action":    "delete_item",
  "tabId":     "01H...",
  "tabName":   "Notifications",
  "sessionId": "sess-abc",
  "item": {
    "id":        "n-7",
    "title":     "New comment",
    "timestamp": "2026-05-19T10:00:00Z"
  },
  "firedAt": "2026-05-20T09:45:00Z",
  "id":      "n-7"
}
```

Webhook: verify ownership, `DELETE FROM notifications WHERE id = 'n-7'`, return 204.


---

# Agent Instructions: Querying This Documentation

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

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

```
GET https://docs.tillforty.com/oaura/tabs/default.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

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