Recipes

Recipes

Copy-paste sequences for the common jobs. Everything below assumes an active connection and a bearer token:

API=https://api.test.geena.eu/partner/v1
REQ=<request_id>          # from the token exchange, or GET $API/requests
AUTH='Authorization: Bearer <access token>'

All of these flows are implemented end-to-end in the public demo apps — github.com/Identa-io/demo is the reference integration.

Read everything the user granted

curl -H "$AUTH" $API/requests/$REQ/status
# for each item with status=granted:
curl -H "$AUTH" $API/requests/$REQ/slots/<slotId>

Serve-on-read means you always get the current version — if the user updates their address in Geena, your next read has it. Do not cache what you were not granted keep for.

Fill a pending slot from the vault

With the user present in your app (the tap is the consent):

curl -H "$AUTH" $API/requests/$REQ/slots/<slotId>/candidates
# render the rows (name + current data), the user taps one:
curl -H "$AUTH" -H 'content-type: application/json' \
  -d '{"resourceId":"<resourceId>"}' \
  $API/requests/$REQ/slots/<slotId>/attach

Create a value the vault doesn’t hold yet

Supply the naming vocabulary yourself (“Personal”, “Work”, “Mobile”) so the user’s vault stays navigable:

curl -H "$AUTH" -H 'content-type: application/json' \
  -d '{"name":"Work","data":{"email":"anna@firm.se"}}' \
  $API/requests/$REQ/slots/<slotId>

Create is always-create: a fresh document, granted in the same act — it never overwrites anything.

Set a one-value-per-person field

Some schemas hold one value per person (PersonFullName, PersonTaxStatus, …). Don’t make the user pick between instances — fill the value:

# 1. candidates — is there an existing document (often an empty starter)?
curl -H "$AUTH" $API/requests/$REQ/slots/<slotId>/candidates
# 2a. one exists: attach it (skip if already granted), then write the value
curl -H "$AUTH" -H 'content-type: application/json' \
  -d '{"resourceId":"<resourceId>"}' $API/requests/$REQ/slots/<slotId>/attach
curl -X PUT -H "$AUTH" -H 'content-type: application/json' \
  -d '{"data":{"firstName":"Anna","lastName":"Ek"}}' \
  $API/requests/$REQ/slots/<slotId>
# 2b. none exists: create
curl -H "$AUTH" -H 'content-type: application/json' \
  -d '{"data":{"firstName":"Anna","lastName":"Ek"}}' \
  $API/requests/$REQ/slots/<slotId>

Prefill your form from the candidate’s data so the user corrects rather than retypes. The write path needs the slot’s edit verb; the create path fill.

Update data later (the manage-account pattern)

The user changed banks and tells you first. Push the update through the connection — their vault stays the source of truth:

# 1. read the current record
curl -H "$AUTH" $API/requests/$REQ/slots/<slotId>
# 2. merge your change over the CURRENT data — a write replaces wholesale
# 3. write the next version
curl -X PUT -H "$AUTH" -H 'content-type: application/json' \
  -d '{"data":{"bankName":"Nya Banken","accountNumber":"SE45…","currency":"SEK"}}' \
  $API/requests/$REQ/slots/<slotId>

Never PUT a partial object: read, merge, write. The user sees the new version in Geena, attributed to your app — and every other connection they granted that document to serves the update on its next read. Skip the write when nothing changed; a no-op version helps nobody.

List the user’s relatives

For manifests with subjects:

curl -H "$AUTH" $API/requests/$REQ/status          # → subjects[] + their slots
curl -H "$AUTH" $API/requests/$REQ/subjects/<subjectId>/candidates
# → persons: [{ "alias": "…", "label": "Alma", "bound": true }, …]

Consent to the manifest is what authorizes this enumeration — it exists only per subject of an active connection, filtered to the declared relation, and it is receipted like every listing.

Add a child and fill their details

# the user types the name in YOUR ui — "stays in your Geena"
curl -H "$AUTH" -H 'content-type: application/json' \
  -d '{"label":"Alma"}' $API/requests/$REQ/subjects/<subjectId>/persons
# → { "alias": "c0ffee12-…", … }   then fill each subject slot for that person:
curl -H "$AUTH" -H 'content-type: application/json' \
  -d '{"person":"c0ffee12-…","data":{"firstName":"Alma","lastName":"Ek"}}' \
  $API/requests/$REQ/slots/<slotId>

Upload a file

curl -H "$AUTH" -F 'label=Driving licence' -F 'file=@licence.jpg' \
  $API/requests/$REQ/slots/<slotId>

Propose the label from the filename minus its extension and let the user edit it. Reading it back: the slot’s record carries a downloadUrl; stream it server-side — never hand your token to the browser.

Use the connection as a login

The second visit needs no consent screen: an active connection plus a live login binding makes the ceremony skippable — the user clicks your button, Geena bounces them straight back with a code, and the token response carries the same request_id. That round-trip is your passwordless login: no password, no email code, and revocation in Geena is session revocation with you. Run the same authorize flow every time; keep no local credential.

Handle revocation honestly

When the user revokes you in Geena, every serve answers 404/403 and GET /status reports the state. Poll it (or react to the first refused call), then:

  • drop everything you were not granted keep for — the grant to hold it is gone;
  • flip your UI to its honest empty state (“access ended — reconnect to continue”), not an error page;
  • offer the connect button again. A re-connect is a fresh consent, never a resurrection.

Build this state on day one. It is the demo every privacy-conscious customer runs first.