Reading & writing data

Reading & writing data

Slot kinds map to three record types: schema slots serve structured documents, id_document slots serve identification documents, personal_files slots serve files. Reading a granted item needs no verb — that is what a connection is; writing and deleting need the slot’s edit verb (visible in /status). Every operation below also requires a grant; a slot the user has not granted answers 404 not_found, indistinguishable from one that does not exist.

Info

Every serve is written to the user’s receipt ledger before the response — reads included. If the receipt cannot be recorded, the call fails. The user sees each access, attributed to your app.

Read a granted slot

GET /partner/v1/requests/{requestId}/slots/{slotId}

Data is decrypted from the user’s vault at call time — Geena stores nothing for you and your access lasts exactly as long as the connection. The response carries one record per grant on the slot:

{
  "slotId": "7c2b9e11-30cf-4f2e-9f57-b8a4f7f4f2ad",
  "label": "Proof of address",
  "kind": "schema",
  "target": "PersonAddress",
  "records": [
    {
      "resourceId": "b7d0…",
      "type": "document",
      "version": 4,
      "name": "Home address",
      "data": { "street": "Keizersgracht 1", "city": "Amsterdam" }
    }
  ]
}

An id_document record instead carries documentType, ocr, verification and images; a file record carries label, fileName, mimeType, fileSize and a downloadUrl — file content never arrives inline. A record answering a subject slot additionally carries a subject block — { "subjectId", "relation", "label", "alias" } — naming which family member it is about (see Relatives & subjects).

Writes and deletes are refused on subject slots: a family member’s data is edited first-party in Geena, never delegated. Fill-surface creates into their vault (with person=) are the one exception — see the relatives page.

Stream file content

GET /partner/v1/requests/{requestId}/slots/{slotId}/files/{fileId}

Exactly the downloadUrl a file record hands you. The file must be granted on that very slot; the response streams the decrypted bytes with the file’s own Content-Type and a Content-Disposition: attachment header.

Write a new version (documents only)

PUT /partner/v1/requests/{requestId}/slots/{slotId}
Content-Type: application/json

{ "data": { "street": "New street 5", "city": "Amsterdam" }, "name": "Home address" }
Warning

A write replaces the document’s data wholesale. Send the complete object, never a patch: read the current record, merge your change over it, and PUT the result — any field you omit is gone in the new version.

Delegated writes update, they never create: the slot must hold exactly one granted structured document, and your write becomes its next version — validated against the document’s own schema (see the built-in catalog), attributed to your app in the version history, receipted before the mutation. name is optional.

{
  "slotId": "7c2b9e11-30cf-4f2e-9f57-b8a4f7f4f2ad",
  "kind": "schema",
  "target": "PersonAddress",
  "resourceId": "b7d0…",
  "version": 5
}

Delete (documents only)

DELETE /partner/v1/requests/{requestId}/slots/{slotId}

The user’s own hard delete, delegated: every version is removed, 204 on success. Platform-protected documents refuse with 403 delete_refused. The receipt ledger outlives the data — the deletion stays attributed to your app.

Legacy addressing

GET|PUT|DELETE /partner/v1/requests/{requestId}/items/{kind}/{target} resolves a slot by its (kind, target) pair — e.g. items/schema/PersonAddress, with the target plain or base64url-encoded. It works only while the pair is unique in the manifest and answers 409 ambiguous_item once it is not. Prefer slot ids from /status; the pair form exists for early integrations.