> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wede.pt/llms.txt
> Use this file to discover all available pages before exploring further.

# Continuity evidence packs

> A signed proof of what happened during an outage, which an auditor can check without wede

A continuity evidence pack is a signed record of one time window of your organisation: every period offline or degraded and how long it lasted, who or what changed each state, every synchronisation that came afterwards with the result of each item, the channels events came through and the integrity incidents. It is made for DORA, CBUAE and internal audits.

The pack is a JSON document signed with ECDSA P-256. The private key lives in a hardware security module (Cloud KMS) and never leaves it. Anyone with the public key can check a pack with openssl, without trusting wede and without access to your account.

> [!NOTE]
>
> A pack never contains business content: no event payloads, no event type codes and no names of people. It holds times, states, channels, counts and results. People and devices appear only as codes of the pack (P1, D1), never as their ids. See [Codes for people and devices](#codes-for-people-and-devices).

## Who can use it

The same roles as the compliance report: `company_admin`, `country_admin`, `wede_tech_ops` and `wede_global_admin`, with a user session. A `company_admin` produces packs for their own organisation. The other roles must say which organisation (`tenant_id`); a `country_admin` only for organisations in their network. Other roles and API keys receive `403 forbidden`.

## Generate a pack

```text theme={null}
POST /v1/compliance/evidence
```

**from** `string` *required*

Start of the window, ISO 8601 with a time zone (for example `2026-10-14T08:00:00Z`).

**to** `string` *required*

End of the window. It must be after `from`, cannot be in the future and the window can be at most 92 days.

**zone\_code** `string`

Optional. Limits zone periods, zone state changes and events to this zone. Organisation periods and synchronisations are always included.

**tenant\_id** `string`

Required for the wede and country roles. Ignored for an organisation's own users.

```bash theme={null}
curl -X POST "https://api.wede.pt/v1/compliance/evidence" \
  -H "Authorization: Bearer <session_token>" \
  -H "Content-Type: application/json" \
  -d '{"from": "2026-10-14T00:00:00Z", "to": "2026-10-15T00:00:00Z"}'
```

The answer is `201` with the pack:

```json theme={null}
{
  "id": "294341ed-...",
  "format": "wede-evidence/2",
  "window": { "from": "2026-10-14T00:00:00.000Z", "to": "2026-10-15T00:00:00.000Z" },
  "zone_code": null,
  "generated_at": "2026-10-15T13:18:20.000Z",
  "sha256": "f7397e3a...",
  "algorithm": "ECDSA_P256_SHA256",
  "key_id": "projects/.../cryptoKeys/evidence-signing-prod/cryptoKeyVersions/1",
  "signature": "MEUCIQ...",
  "public_key_url": "https://api.wede.pt/v1/evidence/public-keys",
  "public_key_pem": "-----BEGIN PUBLIC KEY-----...",
  "payload": "{\"events\":[...],\"format\":\"wede-evidence/2\",...}"
}
```

`payload` is the signed document itself, as a string. `signature` is the DER signature, base64 encoded. Every pack is kept, can never be changed or removed, and its creation is recorded in the audit log as `compliance.evidence_generated`.

| Error | Meaning |
| - | - |
| `400 invalid_window` | `to` is not after `from`, or is in the future |
| `400 window_too_large` | More than 92 days |
| `400 tenant_required` | The wede and country roles must send `tenant_id` |
| `404 zone_not_found` | The zone is not one of the organisation |
| `413 window_too_large` | More than 20000 synchronisation items in the window; choose a shorter one |
| `503 evidence_signing_unavailable` | The pack could not be signed; nothing was stored, try again |

## List and download

| Request | Returns |
| - | - |
| `GET /v1/compliance/evidence` | The latest 100 packs of the organisation, without the payload (wede and country roles add `?tenant_id=`) |
| `GET /v1/compliance/evidence/{id}` | The pack, as when it was created |
| `GET /v1/compliance/evidence/{id}/json` | The signed document, byte for byte (`wede-evidence-{id}.json`) |
| `GET /v1/compliance/evidence/{id}/signature` | The DER signature as a file (`wede-evidence-{id}.sig`) |
| `GET /v1/compliance/evidence/{id}/pdf` | A readable PDF with the SHA-256 and the signature on the first page |
| `GET /v1/evidence/public-keys` | Public, no session: every key version that signed a pack, in PEM |

The PDF is for people. The proof is always the JSON.

## Check a pack without wede

Put three files in one folder: the JSON of the pack, its signature and the public key (from `GET /v1/evidence/public-keys`, the entry whose `key_id` is the `key_id` of the pack, saved as `wede-evidence.pem`). Then run:

```bash theme={null}
openssl dgst -sha256 -verify wede-evidence.pem \
  -signature wede-evidence-<id>.sig wede-evidence-<id>.json
```

The answer must be `Verified OK`. Any change to the JSON, even one character, makes the check fail. Save the JSON exactly as downloaded: opening it in an editor that reformats it breaks the signature.

The SHA-256 printed on the PDF is the hash of the same JSON:

```bash theme={null}
shasum -a 256 wede-evidence-<id>.json
```

## What a pack contains

The document is canonical JSON: keys sorted at every level and no spaces, so the same pack always gives the same bytes.

| Field | Content |
| - | - |
| `format` | `wede-evidence/2` (packs made before 16 October 2026 are `wede-evidence/1`) |
| `pack_id`, `generated_at`, `generated_by` | The pack, when it was made, and the code and role of the person who asked for it |
| `subject` | Organisation, country, environment (always production), window and zone |
| `summary` | Seconds in each state for the organisation and each zone, number of periods and state changes, synchronisation totals, events by channel, integrity incidents |
| `periods` | Every period that touches the window: scope (organisation or zone), state, full start and end, the part inside the window with its seconds, how it opened and closed, and `reconstructed_from` |
| `state_changes` | Each change in the window: for zones, the state before and after and whether it came from a device (with its code) or a person (with their code) |
| `sync.batches` | Every synchronisation call in the window, its source (`event_batch`, `device_queue` or `degraded_frame`), channel, the code of the device and of the person, and every item with its result (`accepted`, `duplicate`, `rejected`) and error code |
| `events` | Event counts by ingest channel (`api`, `sync`, `offline_dispatch`, `sms`, `satellite`, `lora`, `other`), by whether they were generated offline and by status |
| `integrity_incidents` | Each incident in the window, its reason, the code of the device and whether it is open, resolved or dismissed |
| `notes` | How to read the pack, what the codes mean and the verification command |
| `signing` | Algorithm, key version and where the public key is published |

All times are UTC.

### Codes for people and devices

Each person and each device in a pack gets a code in the order it first appears: `P1`, `P2` for people and `D1`, `D2` for devices. The same person or device has the same code everywhere in that pack, including the person who generated it. Codes belong to one pack: `P1` in one pack and `P1` in another can be different people.

The key that says who or what each code stands for is not in the pack and is never shown to your organisation. wede writes it at the same moment as the pack, so there is never a pack without its key, and keeps it only for the evidence period of your organisation. Only the wede team can read it, and every reading is recorded in the audit log as `compliance.evidence_subjects_viewed`. At the end of the evidence period the key is anonymised. The signed pack itself never changes, so it can still be checked with openssl afterwards.

Packs in format `wede-evidence/1`, made before 16 October 2026, stay exactly as they were signed and still contain user and device ids. The PDF reads both formats.

### Reconstructed periods

Periods, synchronisation records and ingest channels are recorded live from 15 October 2026. Earlier periods were rebuilt and say so:

* `reconstructed_from: "audit_log"`: rebuilt from the audit log, with the exact time of each change.
* `reconstructed_from: "current_state"`: rebuilt from the state stored on 15 October 2026; the start time is approximate.

Events created before that date show the channel `not_recorded`.

## Dashboard

In the wede dashboard, **Compliance** has an **Evidence Packs** tab: choose the window and, if you want, a zone, press **Generate signed pack**, and download the JSON, the signature, the public key and the PDF of each pack. **Copy check** copies the openssl command with the file names of that pack.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.