# Group Lifecycle and Synchronization Methods

[Group Hosting Protocol](../README.md) · [Common Method Conventions](conventions.md)

## `group.create`

The calling account creates a group through `group.create` and becomes its owner on success. The creator first generates the group ID, a 32-byte `client_group_secret`, and a member X25519 key pair, computes the client-secret commitment according to [Client and Relay Secrets](../concepts/model-and-keys.md#client-and-relay-secrets), and then seals a client-secret box to its own member public key.

| Item | Convention |
|---|---|
| HTTP | `POST /meshline/v1/group/create` |
| Session requirement | Device session |
| WSS | `group.create` |
| HTTP success status | `204 No Content` |

### Request Parameters

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `create` | GroupCreate | Yes | The complete group-creation object signed by the creator; becomes the sequence 0 event's `payload` unchanged |
| `client_secret_box` | GroupSecretBox | Yes | The creator's client-secret box |

`GroupCreate` fields:

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `$type` | string | Yes | Fixed to `meshline.group.create` |
| `group_id` | string | Yes | A new group ID jointly derived from the initial owner's account, the current hosting relay, and `nonce` according to [Group ID](../core-objects.md#group-id) |
| `nonce` | string | Yes | A 16-byte value generated by the creator for this group according to [Group ID](../core-objects.md#group-id), encoded as unpadded base64url; used to derive the group ID and immutable throughout the group lifecycle |
| `name` | string | Yes | Initial group name; MUST contain at least one non-[whitespace character](../../../general.md#text-whitespace-characters) and be at most 256 bytes in UTF-8 |
| `description` | string | No | Initial description; if nonempty, MUST NOT consist solely of [whitespace characters](../../../general.md#text-whitespace-characters) and MUST be at most 4 KiB (4,096 bytes) in UTF-8 |
| `member_capacity` | integer | Yes | Initial member capacity; MUST be positive and no greater than the maximum group member count declared by `relay.info`; the creator occupies one slot |
| `invite_policy` | string | Yes | Initial member invitation policy; values and meanings are defined by [`GroupState.invite_policy`](../core-objects.md#groupstate) |
| `owner` | object | Yes | Initial owner information |
| `client_secret_commitment` | string | Yes | Initial [client-secret commitment](../concepts/model-and-keys.md#client-and-relay-secrets), using the `sha256:` textual representation |
| `device_signature` | string | Yes | Signed by the initial owner's device used for this call; the signature input is the complete `GroupCreate` excluding this field |

`owner` contains:

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `account` | string | Yes | The account creating the group and becoming its initial owner; MUST be the calling account and the creator account used to derive `group_id` |
| `member_encryption_public_key` | string | Yes | The initial owner's 32-byte X25519 public key, encoded as unpadded base64url |

### Response Object

None.

### Processing and Errors

The relay identifies the calling account and device through the device session, verifies the creation signature, confirms that the account in the device certificate, `owner.account`, and the calling account are identical, verifies that the group ID is derived from that account, the current relay, and `nonce`, and checks the representations of the initial owner's member encryption public key and the client-secret commitment. The creator's key box is validated according to [Client-Secret Boxes](../concepts/model-and-keys.md#client-secret-boxes).

Invalid fixed values, group ID derivation, name, description, capacity, invitation policy, key, commitment, or key-box encoding return `bad_request`. An incompatible session mode or an initial owner other than the calling account returns `forbidden`. An existing group ID or concurrent creation conflict returns `state_conflict`.

On success, the relay creates the member record and the creator device's access interval from the initial owner information in the creation object, fixes the declared client-secret commitment as the group's current commitment, generates the epoch 0 relay secret, saves the initial owner's member public key, the creation event's verification certificate, the epoch 0 key entry, and the initial owner's client-secret box, and atomically appends the sequence 0 creation event.

Signature inputs, management-chain digests, and event bodies MUST preserve the distinction between an omitted field and an explicit empty string; fields MUST NOT be inserted or removed before signature verification or digest computation.

## `group.resolve`

Queries the group's current state for current members or for nonmembers with a valid invitation before joining.

| Item | Convention |
|---|---|
| HTTP | `GET /meshline/v1/group/resolve` |
| Session requirement | Device session |
| WSS | `group.resolve` |
| HTTP success status | `200 OK` |

### Request Parameters

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `group_id` | string | Yes | The group whose current state is requested; MUST be hosted by the current relay |
| `invite_id` | string | No | Invitation ID used by a nonmember to read the pre-join state; current members may omit it, while nonmembers MUST supply an invitation that is still valid and applicable to them |

### Response Object

Returns the current [`GroupState`](../core-objects.md#groupstate).

### Processing and Errors

Current members who are not banned can query the group state directly. Accounts that have not joined MUST supply an invitation that is valid, unrevoked, has remaining uses, and applies to them. Reading the state neither consumes an invitation use nor creates an application. Banned accounts cannot obtain read access through an invitation.

An invalid group ID or supplied invitation ID format returns `bad_request`; a nonexistent group returns `not_found`; a banned caller, or a nonmember without an applicable valid invitation, receives `forbidden`.

## `group.sync`

Reads the group timeline visible to the current device by `sequence`, allowing clients to verify the management chain, reconstruct group state, and process group messages.

| Item | Convention |
|---|---|
| HTTP | `GET /meshline/v1/group/sync` |
| Session requirement | Device session |
| WSS | `group.sync` |
| HTTP success status | `200 OK` |

### Request Parameters

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `group_id` | string | Yes | The group whose events are to be synchronized |
| `after` | integer | No | Read starting position; only events with a greater `sequence` are returned. Omit or use `-1` for the first synchronization. MUST be at least `-1` and no greater than the current timeline head; need not identify an existing event |
| `limit` | integer | No | Maximum events returned in this page; MUST be a positive safe integer, subject to the [pagination conventions](../../methods/conventions.md#pagination) |

### Response Object

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `events` | array&lt;GroupEvent&gt; | Yes | The page of events read; strictly increasing by `sequence`, possibly empty, with no requirement for consecutive numeric values |
| `certificates` | array&lt;DeviceCertificate&gt; | Yes | Historical signing certificates referenced by this page's event `signer_device_id` values; deduplicated by derived device ID |
| `has_more` | boolean | Yes | Whether more readable events existed when this page was generated; MUST be `false` for an empty page |

#### Readable Events and Pagination

Before returning events, the relay MUST confirm that the account and device currently have group read access and return only readable events satisfying the requested position. All management events are readable regardless of device message access intervals; message events are readable only within the device's authorized intervals and while they have not been pruned.

`events` and `has_more` MUST come from the same read snapshot. The relay MUST NOT skip management or message events that remain readable. Pruned messages and messages outside authorized intervals are skipped and do not occupy page entries. When `has_more = true`, continue from the last item's `sequence`; when it is `false`, this paginated read is complete.

A pagination position does not mean that the client has finished verifying and processing the corresponding events. Advancing the synchronization position follows [Group Timeline and Synchronization Rules](../concepts/timeline-and-sync.md#group-timeline-and-synchronization-rules). Sequence gaps alone do not indicate missing events.

#### Event Verification and State Reconstruction

A client without locally verified group state MUST start from `-1` to obtain the creation event, verify the expected group identity according to the [management-chain rules](../core-objects.md#client-verification-and-recovery), and reconstruct state event by event. A client with verified local group state may continue from a synchronization position consistent with that state.

The client validates this page's certificates according to the signature and identity rules of [`DeviceCertificate`](../../core-objects/accounts-and-devices.md#devicecertificate), rejecting responses with duplicate device IDs, missing certificates, or mismatched references. For client-initiated events, it locates the certificate by `signer_device_id` and verifies the `payload` signature using the account and signing public key in that certificate. Relay-initiated rotations are checked against their [body definition](../core-objects.md#relay-secret-rotation-events). Subsequent certificate renewal, expiry, or device removal does not invalidate an original event's signature; historical authorization is determined from preceding group state in the management chain.

Event `sequence` values strictly increase, and `epoch` MUST NOT decrease. Known events that advance keys MUST strictly increase the version; in a complete timeline, events that do not advance keys retain the preceding event's version. A message envelope's `epoch` MUST equal the outer event's value, and the group, device certificate, and message-envelope signature MUST be valid.

#### Key Retrieval and Message Processing

Events and key material can be read independently. Member private keys obtained through same-account messages are validated, stored, and used according to [`GroupMemberPrivateState`](../concepts/account-sync.md#groupmemberprivatestate).

Once a group message envelope passes these checks and the corresponding application secret is available, the client checks the sending-account context and decrypts according to [Group Message Key Derivation, Encryption, and Decryption](../concepts/messaging-and-encryption.md#group-message-key-derivation-and-encryption), then validates the result against its business-object `$type` before processing it.

When the corresponding historical private key or key material is unavailable, the client may persist the verified message envelope pending later decryption. Invalid message signatures, ciphertext authentication failures, and invalid business content may be persisted as rejection results. Required keys are obtained through [`group.key.sync`](keys.md#groupkeysync) under their original key permissions.

### Processing and Errors

The calling account MUST be a current member who is not banned. For a device requiring a new interval, message and key access starting points are established according to [Device Access Intervals](../concepts/membership-and-access.md#device-access-intervals), while allowing it to read all earlier management events. Failed parameter or authorization checks do not establish a new interval.

An invalid group ID, `after`, or `limit`, or a requested position beyond the current timeline head, returns `bad_request`. A nonexistent group returns `not_found`. An account that has left, been removed, or been banned, or a device without current read access, receives `forbidden`.

## `group.close`

The current owner permanently closes the group through `group.close`. No further group timeline events may be appended after closure. Read service and retention of messages and keys after closure are not guaranteed.

| Item | Convention |
|---|---|
| HTTP | `DELETE /meshline/v1/group/close` |
| Session requirement | Device session |
| WSS | `group.close` |
| HTTP success status | `204 No Content` |

### Request Parameters

| Field | Type | Required | Semantics and constraints |
|---|---|---|---|
| `$type` | string | Yes | Fixed to `meshline.group.close` |
| `group_id` | string | Yes | The group the current owner intends to close permanently |
| `prev_hash` | string | Yes | Digest of the preceding [management-chain](../core-objects.md#participating-events-and-predecessor-references) entry; MUST still be the current chain head at submission |
| `device_signature` | string | Yes | Signed by the current owner using the device for this call; the signature input is the complete closure request excluding this field |

### Response Object

None.

### Processing and Errors

Invalid request format or fixed values return `bad_request`; a nonexistent group returns `not_found`; a caller who is not the current owner or is banned receives `forbidden`; concurrent state changes or an already closed group return `state_conflict`.

On success, the operation appends the final closure event, permanently changes the state to `closed`, and closes all current device access intervals. The closure event retains the pre-closure `epoch`.
