# The client portal

**The client portal is your customer's own view of the work you are doing for them.** It is a separate surface, not your dashboard with things hidden: a client signing in lands in the portal and never sees the agency's information architecture at all.

Your dashboard answers *what is the work doing*; the portal answers *what has the agency shared with this customer*. The two are built separately so that nothing internal can reach the portal by accident.

## Prerequisites

- A client collaborator with a grant on at least one project — see [Client access](/clients/access/).
- At least one published deliverable or live deployment. Until then, the portal is empty.

## What the portal contains

| Screen | What it shows |
| --- | --- |
| **Overview** | One row per shared project — its name, how many deliverables you have published, and when it last moved. Plus the most recent activity across all of them. |
| **Deliverables** | The work you published, its progress, and anything waiting on the customer's sign-off. |

Activity is a small, fixed vocabulary. Three things reach a customer's timeline:

| `type` | What happened |
| --- | --- |
| `deliverable.published` | A deliverable was published to them for the first time |
| `deliverable.approved` | They accepted a deliverable |
| `deployment.published` | A deployment they can visit went live |

**Decisions reach a customer through deliverables:** The one decision a customer makes is signing off on a published deliverable. The sign-off is bound to the exact revision they were shown, and can be made only once. See [Deliverables & approvals](/clients/deliverables/).

The agency's own approval queue is separate. A customer cannot see or answer it — see [Roles & permissions](/security/roles-and-permissions/#approvals).

Each entry carries what changed and when. **There is no actor.** The customer is told a deliverable was published, never which agent or which person inside your agency published it.

## What it never shows

Nothing in the portal comes from run history, execution logs, agent runs, prompts, model costs or the audit log. A customer cannot see:

- what a cycle or a task cost you, or how many runs it took
- internal workflow states, timings or run counts
- secrets, environments, integrations, or model configuration
- the approval queue, events, or the audit log
- any project they were not individually invited to — or that such a project exists

**Absence and no-access look the same:** A client with no grants gets an **empty** project list. The portal never discloses that another project exists, and never shows counts of anything the customer cannot see.

## Read it as the agency

The same two endpoints serve both audiences, and for an agency viewer they cover every project in the Workspace rather than only the shared ones. Use them to check what a customer is shown.

**One row per project, plus recent activity.**

```bash
curl -sS "$HLIX_BASE_URL/v1/api/client-portal/overview" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID"
```

Expected output:

```json
{
  "projects": [
    {
      "projectId": "…",
      "name": "Acme storefront",
      "publishedDeliverables": 4,
      "awaitingYourDecision": 0,
      "lastActivityAt": "2026-08-27T16:02:44.000Z"
    }
  ],
  "recentActivity": [
    {
      "id": "deliverable-published-…",
      "type": "deliverable.published",
      "projectId": "…",
      "title": "Checkout redesign",
      "detail": "Ready for your review.",
      "occurredAt": "2026-08-27T16:02:44.000Z",
      "source": { "kind": "deliverable", "id": "…" }
    }
  ]
}
```

`recentActivity` is capped at 20 items of all kinds. `awaitingYourDecision` is always `0` for a customer; sign-off is tracked on the **Deliverables** screen.
  **The complete list, filtered server-side.**

```bash
curl -sS "$HLIX_BASE_URL/v1/api/client-portal/activity?type=deliverable.published&limit=50" \
  -H "x-api-key: $HLIX_API_KEY" \
  -H "X-Organization-Id: $HLIX_WORKSPACE_ID"
```

Expected output — an array of the same activity items:

```json
[
  {
    "id": "deliverable-published-…",
    "type": "deliverable.published",
    "projectId": "…",
    "title": "Checkout redesign",
    "detail": "Ready for your review.",
    "occurredAt": "2026-08-27T16:02:44.000Z",
    "source": { "kind": "deliverable", "id": "…" }
  }
]
```

`limit` defaults to 100 and is capped at 200. Filter here rather than on the overview: the overview's 20-item cap mixes all kinds, so older items of one kind can be crowded out by newer items of another.
  ## If the portal looks wrong

- **A filter returns `[]`.** An unrecognised `type` matches nothing. Check the spelling against the three names above.
- **Your customer sees an empty portal.** They have no grant on any project yet, or they are signed into the wrong Workspace. Accepting an invitation returns the Workspace to switch to.
- **They see a project but no deliverables.** Nothing has been published to them. Work in progress is not visible until you publish it — see [Deliverables & approvals](/clients/deliverables/).
- **You opened a portal URL as an agency member and were sent back.** The portal is for customers only. Use the API calls above to see what a customer is shown.
- **An activity entry you expected is missing.** Only the three kinds above are produced. Anything else your customer needs to know belongs in a deliverable's own summary.

## Where to go next

| If you want to… | Read |
| --- | --- |
| Give someone access to a project | [Client access](/clients/access/) |
| Publish work and request a decision | [Deliverables & approvals](/clients/deliverables/) |
| Charge this customer for the work | [Invoicing your client](/clients/invoicing/) |
| Understand the boundary underneath | [Tenancy & isolation](/security/tenancy/) |

## Next steps

[Client access](/clients/access/)
  [Deliverables & approvals](/clients/deliverables/)
  [Invoicing your client](/clients/invoicing/)
  [Tenancy & isolation](/security/tenancy/)