# Disconnect and errors

> Disconnect your app, react when the venue owner disconnects it, and handle every error code, check, rate limit and body limit of the API.

Your app can end its connection at any time. This page also lists every error, check and limit of the Live 3D API.

## Disconnect

Send a signed `DELETE https://floorplancraft.com/api/connections/self` with no body, signed over zero bytes:

```http
DELETE /api/connections/self HTTP/1.1
Host: floorplancraft.com
X-FPC-Connection: lc_8sKq2mXvT0bN4yR7wE1uZa
X-FPC-Timestamp: 1794054600
X-FPC-Signature: v1=<64 lowercase hex characters>
```

- It answers `204 No Content` and ends the connection. Pushes and embed addresses then answer `403 connection_revoked`, and open live embeds close within a minute.
- The origins your connection added to the plan embed's allowed origins are removed, unless the owner or another connection added them too.
- A repeat answers `204` again. Disconnect works without Live 3D and after the plan is unshared. Limit: 5 at once, then 5 a minute.
- Then delete the `connectionSecret` you stored.

## When the owner disconnects

The venue owner can disconnect your app in the plan's **Share** dialog, under **Connected apps**. Unsharing or deleting the plan ends the connection too. Your next signed request gets `403 connection_revoked`: stop pushing, show that the app is disconnected and offer to connect again.

## Error codes

| Status | `error` | From | What to do |
| --- | --- | --- | --- |
| 400 | `invalid_request` | Connect request, code exchange | Fix the body. A connect request names each problem in `issues`; a code exchange leaves the code unused. |
| 400 | `invalid_grant` | Code exchange | Start the connect flow again. |
| 401 | `unknown_connection` | Signed requests | Check `X-FPC-Connection`. |
| 401 | `stale_timestamp` | Signed requests | Send whole Unix seconds and keep your clock in sync. |
| 401 | `invalid_signature` | Signed requests | Check the signed text, the key and the bytes you send. |
| 402 | `entitlement_required` | Pushes, live embed addresses | The workspace does not have Live 3D: [see below](#live-3d-access). |
| 403 | `connection_revoked` | Signed requests | The connection ended or the plan is no longer shared: stop, and offer to connect again. |
| 403 | `owner_access_required` | Table sync | The person who connected is no longer an editor or admin of the workspace. |
| 404 | `not_found` | Every request | Live 3D connections are turned off on the server. |
| 409 | `conflict` | Table sync | The plan kept changing: try again. |
| 413 | `body_too_large` | Signed requests | Send at most `maxBytes`. |
| 422 | `invalid_feed` | Pushes | Fix the fields in `issues`. Nothing was stored. |
| 422 | `invalid_sequence` | Pushes | `sequence` jumped too far. Nothing was stored. |
| 422 | `invalid_backfill` | Backfill | Fix the fields in `issues`. Nothing was stored. |
| 422 | `backfill_too_large` | Backfill | The day would pass a limit (`limit`, `max`, `needed`). Send fewer floors or events. Nothing was stored. |
| 422 | `invalid_request` | Live embed addresses, table sync | Fix the body. |
| 422 | `plan_too_large` | Table sync | The plan would pass a limit. Nothing was added. |
| 422 | `unsupported_plan` | Table sync | The plan cannot take these tables: add them in the planner. |
| 422 | `unsupported_app` | Table sync | Booking and play venue apps cannot add tables. |
| 429 | `rate_limited` | Every request | Wait `retryAfterMs` milliseconds. |
| 503 | `unavailable` | Every request | Try again shortly. A code exchange keeps its code. |

## Issues

A `400` or `422` with `issues` names each problem:

```json
{ "error": "invalid_feed", "issues": [{ "path": "tables.3.state", "message": "…" }] }
```

`path` names the field, counting from 0: `tables.3.state` is the fourth table's `state`. An empty `path` means the whole body. A connect request lists up to 50 issues and a push up to 20.

## Live 3D access

```json
{
  "error": "entitlement_required",
  "entitlement": "live_view",
  "upgradeUrl": "https://floorplancraft.com/checkout/live-3d?connection=lc_8sKq2mXvT0bN4yR7wE1uZa"
}
```

A `402` means the venue's workspace does not have Live 3D. Open `upgradeUrl` in a browser tab for the venue owner, who signs in and subscribes there. Do not parse or build the address yourself. Until then pushes are not stored: show the plan embed instead of the live view.

## Check order

Signed requests are checked in this order, and the first failure answers:

1. The connection feature is on (`404 not_found`) and can run (`503 unavailable`).
2. Your server's address has not used up its allowance of failed requests (`429`).
3. `X-FPC-Connection` is `lc_` and 22 base64url characters (`401 unknown_connection`).
4. The body is within the endpoint's size limit (`413 body_too_large`).
5. `X-FPC-Timestamp` is within 300 seconds of the server's clock (`401 stale_timestamp`).
6. The connection exists (`401 unknown_connection`) and the signature matches (`401 invalid_signature`).
7. The connection is active (`403 connection_revoked`).
8. The connection is within its own rate limit (`429`).
9. The workspace has Live 3D (`402 entitlement_required`).
10. The plan is still shared (`403 connection_revoked`).
11. The body is valid (`422`).

Failures in steps 3 to 6 count against your server's address. Disconnect skips steps 7, 9 and 10. Table sync skips step 9 and checks its body before step 10.

## Rate limits

| Request | Limit |
| --- | --- |
| Connect request | 20 at once, then 60 an hour from one address (an IPv6 address counts by its /64); 10 at once, then 30 an hour for one install from one address; 120 at once, then 60 a minute from everyone together; at most 1,000 requests waiting for an owner |
| Push | 24 at once, then 12 a minute per connection |
| Live embed address | 10 at once, then 30 an hour per connection |
| Table sync | 6 at once, then 6 a minute per connection |
| Disconnect | 5 at once, then 5 a minute per connection |
| Failed signed requests and code exchanges | 60 at once, then 60 a minute from one address |

Every `429` has `retryAfterMs` in its body and a `Retry-After` header in seconds.

## Body limits

| Request | Largest body |
| --- | --- |
| Connect request | 256 KB; larger is `400 invalid_request` |
| Code exchange | 4 KB; larger is `400 invalid_request` |
| Push | 512 KB; larger is `413` with `"maxBytes": 524288` |
| Live embed address | 4 KB; larger is `413` |
| Table sync | 256 KB; larger is `413` |
| Disconnect | Send no body |

## Related

- [Signing requests](https://floorplancraft.com/docs/signing-requests.md)
- [Live updates](https://floorplancraft.com/docs/live-updates.md)
- [Connect an app](https://floorplancraft.com/docs/connect-an-app.md)

---

Source: https://floorplancraft.com/docs/disconnect-and-errors · Updated 2026-11-07
