> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.adid.dev/llms.txt.
> For full documentation content, see https://docs.adid.dev/llms-full.txt.

# Rotate Key

##### When to use

* You suspect (or know) the key has been compromised.
* The platform's recommended rotation cadence (90 days for high-risk DIDs).
* You are migrating from secp256k1 to Ed25519 for credential signing.

##### Before you begin

* You own the DID.
* You will not need the *old* key for anything in the next 5 minutes (the rotation is immediate; old key is no longer authoritative).

##### Steps

1. Open `/dids/:did` and click **Rotate key**.
   2\. Confirm the key type for the new key (defaults to the same as the existing one).
2. Click **Rotate**. The portal calls `POST /api/v1/dids/{did}/rotate-key` (`did.go:197`).
3. Server-side:
   * Verifies caller owns the DID.
   * Generates a new keypair.
   * Builds a new DID Document with the new `verificationMethod`.
   * Submits `updateDID` to the DIDRegistry signed by the *current* (about-to-be-rotated) key. (After this transaction, the old key is no longer in `verificationMethod`.)
   * Inserts the old key into `did_key_history` for audit/replay diagnostics.

##### API & SDK

<table>
  <tr>
    <th>cURL</th>

    <th>TypeScript SDK</th>
  </tr>

  <tr>
    <td>
      ```bash
      curl -X POST https://adid.dev/api/v1/dids/did:adi:0x9a2c.../rotate-key \
        -H "Authorization: Bearer $ACCESS_TOKEN"
      ```
    </td>

    <td>
      ```ts
      await client.rotateKey('did:adi:0x9a2c...', 'Ed25519');
      // re-resolve to inspect the new verificationMethod
      const updated = await client.resolveDID('did:adi:0x9a2c...');
      ```
    </td>
  </tr>
</table>

##### Verify

Re-resolve the DID. The `verificationMethod` should now contain the new `publicKeyMultibase`. The `updated` field on the DID Document metadata should reflect the new timestamp.

##### Troubleshooting

| Code                         | Cause                                 | Fix                                            |
| ---------------------------- | ------------------------------------- | ---------------------------------------------- |
| `403 NOT_DID_OWNER`          | Caller is not the controller          | Sign in with the right account.                |
| `409 KEY_ROTATION_IN_FLIGHT` | A previous rotation has not finalised | Wait 30 s and retry.                           |
| `503 CHAIN_UNREACHABLE`      | RPC down                              | Retry; the API has idempotent retry semantics. |

> ⚠️ **Warning** — Any active credential **signed by the old key** remains valid as long as the old key is in the DID's key history. Verifiers SHOULD check `did_key_history` (exposed via the DID Document metadata) when validating older VCs. The Verification Engine in the portal does this automatically.

#### 3.1.6. Deactivating a DID ##### When to use

You no longer want anyone to trust this DID for new credentials/presentations. Deactivation is a soft-delete: the DID Document remains resolvable but is flagged `deactivated: true`, and the platform refuses to use it for issuance, presentation, or DIDComm.

##### Before you begin

* You own the DID.
* You have no active credentials *issued* by this DID that you intend to revoke first (revoke them before deactivating; afterwards the deactivated DID cannot be used to sign revocation entries).

##### Steps

1. Open `/dids/:did`.
2. Click **Deactivate** (red button at the bottom).
3. Confirm in the modal — type the DID into the confirmation field.
4. The portal calls `DELETE /api/v1/dids/{did}` (`did.go:166`). The handler emits `DIDDeactivated` on the registry.

##### API & SDK

<table>
  <tr>
    <th>cURL</th>

    <th>TypeScript SDK</th>
  </tr>

  <tr>
    <td>
      ```bash
      curl -X DELETE https://adid.dev/api/v1/dids/did:adi:0x9a2c... \
        -H "Authorization: Bearer $ACCESS_TOKEN"
      ```
    </td>

    <td>
      ```ts
      await client.deactivateDID('did:adi:0x9a2c...');
      ```
    </td>
  </tr>
</table>

##### Verify

Resolution still works, but the document metadata shows `deactivated: true`. Per [W3C DID Core 1.0 §5.2](https://www.w3.org/TR/did-core/#did-document-metadata), this is the canonical end state.

##### Troubleshooting

| Code                           | Cause                                       | Fix                                                           |
| ------------------------------ | ------------------------------------------- | ------------------------------------------------------------- |
| `403 NOT_DID_OWNER`            | Caller is not the controller                | Sign in correctly.                                            |
| `409 ALREADY_DEACTIVATED`      | DID was already deactivated                 | No action needed.                                             |
| `400 ACTIVE_CREDENTIALS_EXIST` | The DID is the issuer of active credentials | Revoke or transfer first; see [§4.4](#revocation-management). |

#### 3.1.7. Adding & Revoking Delegates ##### When to use

A delegate is a *different* DID authorised to act on behalf of yours for a specific purpose (e.g. an agent DID that may sign DIDComm messages on your behalf, or an organisational sub-DID that may issue credentials under your authority). Delegation is the cleaner alternative to sharing keys.

##### Before you begin

* You own the principal DID.
* The delegate's DID is resolvable.

##### Steps

1. Open `/dids/:did` and scroll to **Delegates**.
2. Click **Add delegate**.
   3\. Fill:
   * **Delegate** (`delegate`) — the delegate's DID or address, e.g. `did:adi:0x4f3a...`.
   * **Type** (`delegateType`) — e.g. `sigAuth`, `veriKey` (per ERC-1056 conventions).
   * **Validity** (`validity`) — number of seconds (default 86400 = 24 h).
3. Save. The portal calls `POST /api/v1/dids/{did}/delegates` (`did.go:290`). The body is the params object directly: `{ delegateType, delegate, validity }`.

##### API & SDK

<table>
  <tr>
    <th>cURL — add</th>

    <th>cURL — revoke</th>
  </tr>

  <tr>
    <td>
      ```bash
      curl -X POST https://adid.dev/api/v1/dids/did:adi:0x9a2c.../delegates \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "delegateType":"sigAuth",
          "delegate":"did:adi:0x4f3a...",
          "validity": 86400
        }'
      ```
    </td>

    <td>
      ```bash
      curl -X DELETE https://adid.dev/api/v1/dids/did:adi:0x9a2c.../delegates \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{ "delegateType":"sigAuth", "delegate":"did:adi:0x4f3a..." }'
      ```
    </td>
  </tr>
</table>

##### Verify

```bash
curl -H "Authorization: Bearer $ACCESS_TOKEN" \
  https://adid.dev/api/v1/dids/did:adi:0x9a2c.../delegates
```

Returns an array `[{ delegate, delegateType, validUntil }]`.

##### Troubleshooting

| Code                     | Cause                                   | Fix                                     |
| ------------------------ | --------------------------------------- | --------------------------------------- |
| `400 INVALID_DELEGATE`   | DID format invalid                      | Check DID syntax.                       |
| `404 DELEGATE_NOT_FOUND` | DID not resolvable                      | Add the DID to a public registry first. |
| `409 DELEGATE_EXISTS`    | Same delegate + type already authorised | Revoke first, or use a different type.  |

#### 3.1.8. DID Attributes ##### When to use

You want to attach a small piece of structured data to your DID — e.g. a service URL, a name, a hash — without modifying the DID Document directly. This mirrors the ERC-1056 attribute mechanism: signed key/value pairs anchored on chain.

##### Before you begin

* You own the DID.
* The attribute name is short (≤32 bytes) and meaningful.

##### Steps

1. Open `/dids/:did` and scroll to **Attributes**.
2. Click **Set attribute**.
3. Enter:
   * **Name** — e.g. `did/svc/HubService`, `did/auth/email`.
   * **Value** — string or hex-encoded bytes.
   * **Validity** — seconds.
4. Save. Calls `POST /api/v1/dids/{did}/attributes` (`did.go:369`).

##### API & SDK

<table>
  <tr>
    <th>cURL — set</th>

    <th>cURL — revoke</th>
  </tr>

  <tr>
    <td>
      ```bash
      curl -X POST https://adid.dev/api/v1/dids/did:adi:0x9a2c.../attributes \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "name":"did/svc/AgentCard",
          "value":"https://my.agent/.well-known/agent.json",
          "validity":2592000
        }'
      ```
    </td>

    <td>
      ```bash
      curl -X DELETE https://adid.dev/api/v1/dids/did:adi:0x9a2c.../attributes \
        -H "Authorization: Bearer $ACCESS_TOKEN" \
        -H "Content-Type: application/json" \
        -d '{
          "name":"did/svc/AgentCard"
        }'
      ```
    </td>
  </tr>
</table>

##### Verify

Resolve the DID; the attribute is exposed via the `didDocumentMetadata.attributes` array (or whatever the resolver returns — see [§3.1.9](#did-resolver)).

##### Troubleshooting

If a value isn't appearing, the most common cause is a stale cache. Force a re-resolve with:

```bash
curl https://adid.dev/api/v1/dids/resolve/did:adi:0x9a2c...?refresh=true
```

#### 3.1.9. Universal DID Resolver Tool ##### When to use

You need to fetch the DID Document for *any* DID — yours, someone else's, or a DID from a different method entirely. The universal resolver is the public, no-auth endpoint that does this.

##### Before you begin

Nothing required. The endpoint is unauthenticated.

##### Steps

1. Open the **DID Resolver** page in the portal (`/tools/did-resolver`) — or just call the endpoint directly.
2. Paste the DID into the input field.
3. Click **Resolve**.
4. The portal calls `GET /api/v1/dids/resolve/{did}` (`did.go:29`, registered at `router.go:74`).

##### Supported methods

| Method     | How it resolves                   | Source                         |
| ---------- | --------------------------------- | ------------------------------ |
| `did:adi`  | DIDRegistry.resolveDID()          | ADI chain (cached in Postgres) |
| `did:key`  | Multibase decode of public key    | local computation              |
| `did:web`  | HTTPS GET `/.well-known/did.json` | DNS + HTTPS                    |
| `did:ethr` | ERC-1056 EthereumDIDRegistry      | Ethereum mainnet RPC           |

##### API

<table>
  <tr>
    <th>cURL</th>

    <th>TypeScript SDK</th>
  </tr>

  <tr>
    <td>
      ```bash
      curl https://adid.dev/api/v1/dids/resolve/did:adi:0x9a2c...
      ```
    </td>

    <td>
      ```ts
      // Universal resolver — works for did:adi, did:key, did:web, did:ethr.
      const { didDocument, didDocumentMetadata } =
        await client.resolveAny('did:adi:0x9a2c...');

      // Or, for did:adi only (authenticated):
      // const didDocument = await client.resolveDID('did:adi:0x9a2c...');
      ```
    </td>
  </tr>
</table>

Response shape (per [W3C DID Resolution](https://www.w3.org/TR/did-core/#did-resolution)):

```json
{
  "didDocument":         { "@context":[...], "id":"did:adi:0x9a2c...", ... },
  "didDocumentMetadata": { "created":"...", "updated":"...", "deactivated":false, "txHash":"0x..." },
  "didResolutionMetadata":{ "contentType":"application/did+ld+json" }
}
```

##### Verify

Compare the returned key fingerprint against an out-of-band channel (e.g., the issuer's website, a printed business card). DID Documents are public, but their *binding* to a real-world entity is your responsibility.

##### Troubleshooting

| Code                  | Cause                      | Fix                                         |
| --------------------- | -------------------------- | ------------------------------------------- |
| `404 DID_NOT_FOUND`   | DID not registered         | Verify spelling; check method support.      |
| `502 UPSTREAM_ERROR`  | did:web HTTPS fetch failed | Check the host's `/.well-known/did.json`.   |
| `410 DID_DEACTIVATED` | DID is deactivated         | The document is still returned but flagged. |