P1 APIs provides export and import endpoints that let operators migrate sites between environments without requiring database access on either side. Bundles are portable ZIP files containing all site content — branches, documents, version history, and publish state.
- Content from all workstreams with parent relationships
- All documents (excluding _registry/ which rebuilds automatically)
- For Live workstream: every published version plus the current unpublished version
- For other workstreams: the latest version
- Users list (informational — must be configured manually on target)
- Collaborative editing history — the editor initialises from the latest snapshot on first open
- Users and agent roles — see requiresSetup in the import response
- Site API tokens — must be regenerated on the target
- Allowed origins — must be configured on the target
- A sat_ token with read:all scope on the source site
- A sat_ token with write:create scope on the target site
- An empty Target site (no documents, no non-main branches)
You can generate an access token from the P1 admin, or the P1 API as follows:
1. Authenticate to the P1 backend for the site
2. POST /api/sites/{siteId}/tokens with body: {"name": "migration", "scopes": ["write:create"]}
3. Store the returned token value — it is only shown once
GET /api/admin/sites/{siteId}/export
Authorization: Bearer sat_<read:all or write:create token>Response:
{
"downloadUrl": "https://...",
"bundleKey": "{siteId}/{timestamp}.zip",
"bundleSignature": "<hmac-sha256-hex>",
"exportedAt": "2026-05-28T...",
"documentCount": 21,
"branchCount": 5
}
Download the ZIP from downloadUrl and save the bundleSignature — you will need it for the import step.
POST /api/admin/sites/{targetSiteId}/import
Authorization: Bearer sat_<write:create token>
Content-Type: multipart/form-dataForm fields:
- file: the ZIP bundle downloaded in Step 1
- bundleSignature: the HMAC signature from the export response (strongly recommended)
Response:
{
"importKey": "import:{siteId}:{exportedAt}",
"completedPhases": ["site", "branches", "document:home", ...],
"documentCount": 21,
"sourceSiteId": "03499be6-...",
"crossSiteImport": true,
"requiresSetup": {
"collaborators": {
"users": [{"email": "chris@example.com", "role": "admin"}],
"agents": [{"name": "Zappy AI Assistant", "role": "editor"}]
}
}
}The requiresSetup.collaborators field lists every user and agent that had a role on the source site. Add them to the target site manually using the site collaborator management UI or API.
If the import is interrupted, re-run the same POST request with the same ZIP file and bundleSignature. The import tracks progress keyed to the bundle's exportedAt timestamp. Already-completed phases are skipped automatically. The target site does not need to be empty for a resume — only for a first-time import.
The bundle contains a SHA-256 manifest (bundle.json) covering every file. The bundleSignature provides an additional HMAC layer: if either the bundle files or bundle.json itself are modified after export, the import will reject the bundle with a 422 Signature verification failed error.
# Export
EXPORT=$(curl -s -X GET "https://{P1-API-URL}/api/admin/sites/{siteId}/export" \
-H "Authorization: Bearer sat_...")
DOWNLOAD_URL=$(echo $EXPORT | jq -r .downloadUrl)
SIGNATURE=$(echo $EXPORT | jq -r .bundleSignature)
curl -o bundle.zip "$DOWNLOAD_URL"
# Import
curl -X POST "https://{P1-API-URL}/api/admin/sites/{targetSiteId}/import" \
-H "Authorization: Bearer sat_..." \
-F "file=@bundle.zip" \
-F "bundleSignature=$SIGNATURE"