# Agency client portal

> Show each client the boards your agency shared with them, with a picture of each.

Source: https://mockflow.com/developers/examples/oauth/client-portal

An agency's client portal already has the invoices, the contracts and the messages. The work itself, the wireframes, the moodboard and the roadmap, lives on MockFlow boards shared with the client. This example brings them into the portal: each client connects their own MockFlow account, and sees the boards shared with them, with a picture of each and a link to open it.

[**Download the code**](/developers/assets/examples/mockflow-client-portal.zip) (Node.js 18 or later, no packages)

## What it does

- Lists the boards the client can open but does not own, newest first: the ones the agency shared with them.
- Shows a picture of each board, exported once and reused until the board changes.
- Links each board to MockFlow, where the client can view and comment with their role.

## How it works

1. The client connects through the [OAuth flow](/developers/concepts/oauth-apps) with the scopes `user:read boards:read`.
2. [List their boards](/developers/reference/boards/list-boards) with `GET /boards?sort=updatedAt&order=desc`. Boards shared with them are included, and `role` tells them apart from their own.
3. [Export each board](/developers/reference/boards/export-board) as a PNG and [wait for the job](/developers/reference/jobs/get-job). Reviewers can export, as in the app.
4. Cache the picture with the board's `updatedAt`, and export again only when that changes.

Each picture is published at an unguessable address that anyone with the link can open, so show it only to the client it belongs to.

## Run the example

Register an app in the **OAuth apps** tab of the [developer console](/developers/console/?tab=oauth) with the redirect URI `http://localhost:3000/oauth/callback` and the scopes `user:read` and `boards:read`. Then connect as someone a board has been shared with:

```bash
cp .env.example .env        # paste the client ID and secret into .env
node server.js              # open http://localhost:3000
```

## The key code

```javascript
const shared = [];
for await (const b of mf.paginate('/boards?sort=updatedAt&order=desc')) {
  if (b.role !== 'owner') shared.push(b);
}

async function preview(mf, board) {
  const cached = previews.get(board.id);
  if (cached && cached.updatedAt === board.updatedAt) return cached.imageUrl;
  const job = await mf.post(`/boards/${board.id}/export`, { target: 'board', scale: 1 });
  const done = await mf.waitForJob(job);
  previews.set(board.id, { updatedAt: board.updatedAt, imageUrl: done.result.imageUrl });
  return done.result.imageUrl;
}
```

## Adapt it

- **Freelancers:** a project page per client with the latest designs.
- **Schools:** a parent portal showing a class's shared boards.
- **Partners:** a partner portal with the joint roadmap and launch plans.

## What you need

An OAuth app with the scopes `user:read` and `boards:read`. Works for clients on every plan, including Basic.
