# Relationship tags

Replace private labels on an outgoing connection.


**Auth:** Source-device signed action `set-edge-tags`.
## Request and response

Angle-bracket values describe placeholders, including nested objects and numeric types; these are wire shapes, not captured service data. Replace them before sending.

```http
POST /v1/edges/<edge-id>/tags HTTP/1.1
Host: trust-staging.rebellion.systems
Content-Type: application/json

{
  "action": {
    "action": "set-edge-tags",
    "graphKeyId": "<source-device-id>",
    "edgeId": "<edge-id>",
    "tagsFormat": "plain-v1",
    "tags": [
      "family"
    ],
    "expectedRevision": 0,
    "nonce": "<fresh-random-nonce>",
    "signedAt": "<current-ISO-timestamp>"
  },
  "signature": "<base64-action-signature>"
}
```

Response shape:

```json role="response"
{
  "edge": "<updated-edge-with-owner-tags>",
  "graphEpochRoot": "<graph-root>",
  "poseidonEpochRoot": "<poseidon-root>",
  "eventLogRoot": "<log-root>",
  "eventLogSize": "<nonnegative-integer>"
}
```




`POST /v1/edges/:edgeId/tags` replaces the complete label set for a current active outgoing edge. Authenticate with a usable graph-purpose device of the source node.

```json role="request"
{
  "action": {
    "action": "set-edge-tags",
    "graphKeyId": "source-device-id",
    "edgeId": "current-edge-id",
    "tagsFormat": "plain-v1",
    "tags": ["close friends", "family"],
    "expectedRevision": 0,
    "nonce": "fresh-random-nonce",
    "signedAt": "2026-11-04T12:00:00.000Z"
  },
  "signature": "base64-action-signature"
}
```

Read [the own edge list](/api/edges) first. Its `edgeTags` capability contains `enabled`, `formats`, `maxTags`, `maxCodePoints` and `keyRule`. Supported outgoing metadata contains `tagsFormat: "plain-v1"`, `tags` and `tagsRevision`. Missing metadata means unavailable, not an empty list.

| Parameter | Rule |
| --- | --- |
| `tagsFormat` | `plain-v1` |
| `tags` | At most 16 labels, each 1–64 Unicode code points |
| Text | NFC and canonical whitespace; matching uses `lower-v1` |
| Duplicates | Duplicate matching keys are rejected |
| `expectedRevision` | Current revision, integer 0–2147483645 |

Normalize on explicit Add or Save before signing, using the SDK tag helpers. The server does not rewrite signed text. `tags: []` explicitly clears the set. Success returns `{edge, graphEpochRoot, poseidonEpochRoot, eventLogRoot, eventLogSize}`. Tag edits advance the event log, not the graph commitment roots.

Tags follow the directed node pair through rescoring, device changes and expiry. Expired edges are read-only. Explicit edge removal clears tags; recreating trust starts empty.

## Conflicts and retries

A stale revision returns `EDGE_TAG_REVISION_CONFLICT`. Other documented codes include `EDGE_SUPERSEDED`, `EDGE_REVOKED`, `EDGE_EXPIRED`, `EDGE_TAG_FORMAT_UNSUPPORTED`, `EDGE_TAGS_DISABLED` and `EDGE_TAG_INVALID`.

If the response is lost, re-list. Exactly one revision advance with the intended set means success. An unchanged revision permits a newly signed retry; another advance is a conflict. Do not automatically resend the old signed mutation.


**Does not establish**
Tags are hidden from other graph users, including the recipient, but the service can read them. They are not end-to-end encrypted or part of a zero-knowledge certificate, and they grant no resource access by themselves. There is no server-side tag search.
