---
title: "Hand a project to a client"
description: "End an agency job cleanly. Eject to plain provider code, transfer the project to the client's organisation on free view-only seats, and hand over evidence."
section: "guides"
status: "Planned for 0.7"
spec: "teams-plan"
canonical: "https://mainsdk.dev/docs/guides/client-handover"
updated: "2026-10-28"
---

# Hand a project to a client

> End an agency job cleanly. Eject to plain provider code, transfer the project to the client's organisation on free view-only seats, and hand over evidence.

Status: Planned for 0.7 · Canonical: https://mainsdk.dev/docs/guides/client-handover · Updated: 2026-10-28

At the end of a job you give the client one of two things: code that no longer needs MainSDK, or a project they can read in their own organisation, plus evidence that the app meets the standards you agreed. This page shows the three steps and what each one needs. Plan about an hour for the eject or the transfer, and longer if the client wants an assurance bundle.

> **Note:** Eject is planned for milestone 0.3. Project transfer, view-only seats and `mainsdk assure` are planned for milestone 0.7. Until then the commands below do not run; the steps are the planned design (specs `eject`, `teams-plan`, `assure`).

## What you hand over

| What you hand over | Command | Milestone | Status |
| --- | --- | --- | --- |
| Plain provider code, no MainSDK dependency | `mainsdk eject --all` | 0.3 | Planned, beta. Recipes for email, pay, auth and db are proposed; the final list is not decided. |
| The project in the client's organisation, with history, policy and launch reports | `mainsdk project transfer` | 0.7 | Planned |
| Client people reading the project on free view-only seats | `mainsdk org invite --role viewer` | 0.7 | Planned |
| Signed evidence an assessor can verify | `mainsdk assure`, `mainsdk assure verify` | 0.7 | Planned, beta |

## You need

- A clean git working tree and a passing test suite.
- For the transfer: a Teams organisation, and the client's organisation name.
- For the evidence: the standards or industry pack the client asked for (for example `pci-dss-4.0.1`).

## 1. Leave MainSDK (optional)

Choose this when the client wants no dependency on MainSDK. Plan first, then run it. See [Eject](https://mainsdk.dev/docs/guides/eject).

```bash
npx mainsdk eject --all --dry-run
npx mainsdk eject --all
```

You should see: your tests pass with the same count as before, `main.yaml`, the gates and every `mainsdk` and `@mainsdk/*` dependency are gone, and `EJECT-REPORT.md` lists each guarantee the client no longer gets. Give the client that report.

Be honest with the client about coverage. The spec says eject recipes cover the reference architecture drivers first, and the interfaces with recipes in 0.3 are still an open question. The proposal is email, pay, auth and db. Any other interface has no recipe yet [TBD], so plan to rewrite those calls by hand, or use step 2 instead.

## 2. Transfer the project to the client

Choose this when the client keeps using MainSDK. The transfer moves the project with its history, policy and launch reports in one step and keeps the project ID. The client's people read it on free view-only seats; client viewers never count as active developers (see [Free in the app, paid in the Cloud](https://mainsdk.dev/docs/concepts/free-and-cloud)).

```bash
npx mainsdk org invite ana@client.example --role viewer
npx mainsdk project transfer prj_01JC8Q… --to client-co --keep-maintainer
```

- `--keep-maintainer` keeps one maintainer seat for your agency, so you can still fix things.
- `--bill-addons-to client` puts any Teams add-ons on the client's invoice instead of yours.

You should see: the project in `client-co` with its history, the same project ID, and the client's people able to read it without a charge.

## 3. Hand over evidence

Run `mainsdk assure` against the standards the client asked for, before you eject or transfer. Running it and reading the results is free.

```bash
npx mainsdk assure --standard pci-dss-4.0.1
npx mainsdk assure verify .mainsdk/assure/latest.bundle
```

You should see: the failing controls with owners and fixes, then a verified signature on the bundle, which holds the results, SBOM, VEX, policy test results, drill records and exceptions. Hand the client the bundle. Whether `mainsdk assure` still runs after a full eject is [TBD].

## Verify

Before the handover, run the check and save its output with the evidence.

```bash
npx mainsdk check --env prod
```

A pass exits 0. After an eject, run your test suite instead, since the check is gone with MainSDK.

## If it fails

| Code | Cause | Fix |
| --- | --- | --- |
| `MAIN-ASSURE-0003` | `mainsdk assure` pointed at a URL found a tampered `/.well-known/mainsdk.json` (signature invalid, exit 2). | Do not hand it over. Rebuild the record from your own repo and run assure again. |
| `MAIN-CONFIG-0004` | A literal secret in `main.yaml`, found before you hand the repo over. | Move it to a `secret:` reference and rotate the secret. |

Run `npx mainsdk explain <code>` for the cause and fix offline.

## For agents

```text
Goal: prepare this project for handover to a client.
Read: docs/guides/eject.md, docs/specs/eject.md, docs/specs/teams-plan.md, docs/specs/assure.md.
Touch only: files that eject rewrites, EJECT-REPORT.md, and .mainsdk/assure/.
Do: run mainsdk assure and keep the bundle; run mainsdk eject --all --dry-run and show the plan to the owner;
run the real eject only after the owner approves; never run project transfer without the owner.
Verify: the test suite passes with the same count, and npx mainsdk assure verify .mainsdk/assure/latest.bundle succeeds.
Never weaken, skip or loosen a check to make it pass.
```

## Next

- [Eject](https://mainsdk.dev/docs/guides/eject)
- [Free in the app, paid in the Cloud](https://mainsdk.dev/docs/concepts/free-and-cloud)
- [Make your app enterprise ready](https://mainsdk.dev/docs/guides/enterprise-ready)
