Skip to main content

Contract Rows and Roles

Read one contract row's parties on a day, request an AI read of its documents, add a role, and confirm, edit or reject a role with period and evidence.

A contract row is addressed by productId (the data product) and rowId (the data product record id, which is text and not necessarily a UUID). GET /v1/parties/rows/{productId}/{rowId} returns the row's roles with their parties and evidence, the computed view for a day, the row's problems and the state of its AI read. A row the key's minting user may not see answers 404. on is the day to evaluate (YYYY-MM-DD) and defaults to today in Europe/Berlin.

The view answers who the contract is with on that day. partner, manager, tenant, previous_owner and new_owner are the parties in those roles on the day; effective_partner is a tenant who enters the contract while the tenant's period covers the day, else the partner; addressee is whom a letter goes to (the effective partner's Verwalter when it has one, else the effective partner); owner_addressee is whom a letter about ownership goes to. Each is a party view with address_lines, the postal block as a letter prints it. view.state is confirmed when every role read is confirmed, suggested when any is a suggestion, and none without a partner.

GET/v1/parties/rows/{productId}/{rowId}

Parameters

productId*string (uuid)The data product id.
rowId*stringThe data product record id (text, up to 200 characters).
onstring (date)The day to evaluate, YYYY-MM-DD. Today in Europe/Berlin when omitted.

Response fields

rowobjectdata_product_id, product_name, row_id, label and document_ids (the row's documents, anchor first).
onstring (date)The evaluated day.
roles[]arrayThe row's roles, each with its party.
viewobjectThe computed view: partner, manager, tenant, previous_owner, new_owner, effective_partner, addressee, owner_addressee, contacts, state.
issues[]arrayThe row's problems: code, detail, params, role_ids, party_ids.
readobjectThe AI read: status (pending, running, done, failed), read_at, error, notes, read_with.
cost_unitobjectdocuments (the cost unit the documents print) and quote.

Response: The effective partner on a day (excerpt)

{
  "on": "2026-06-01",
  "view": {
    "on": "2026-06-01",
    "partner": { "display_name": "Vermietung Nord GmbH", "role": "partner", "state": "confirmed" },
    "tenant": { "display_name": "Anna Beispiel", "role": "tenant", "state": "confirmed", "valid_from": "2025-04-01", "valid_to": null },
    "effective_partner": { "display_name": "Anna Beispiel", "role": "tenant", "state": "confirmed" },
    "addressee": { "display_name": "Anna Beispiel", "address_lines": ["Anna Beispiel", "Gartenweg 4", "12345 Musterstadt"] },
    "state": "confirmed"
  },
  "issues": [],
  "read": { "status": "done", "read_at": "2026-05-28T10:02:11.000Z", "error": null, "notes": [], "read_with": null }
}

Requesting an AI Read

POST /v1/parties/rows/{productId}/{rowId}/read queues one AI read of the row's documents. It costs a model call. The read runs in the background and its result is merged with the mapped fields and the register as suggestions; every document claim carries its quote, and a claim whose quote does not occur in the document it names stays low confidence. A read already pending or running is not queued again. The response is 201 with the status (pending or running). A row without documents is refused with 422.

POST/v1/parties/rows/{productId}/{rowId}/read

Adding and Deciding Roles

POST /v1/parties/rows/{productId}/{rowId}/roles gives an existing party a role on the row. The role is confirmed unless state says suggested; when the party already holds the role on the row, that role is updated instead. party_id and role are required. represents_id names another role on the same contract that this role acts for (for a Verwalter or contact), valid_from and valid_to give the period, and enters_contract only counts for the tenant role.

POST/v1/parties/rows/{productId}/{rowId}/roles

Body fields

party_id*string (uuid)The party that gets the role.
role*stringpartner, our_party, manager, tenant, previous_owner, new_owner, contact or invoice_recipient.
represents_idstring (uuid) | nullAnother role on the same contract that this role acts for.
valid_from, valid_tostring (date) | nullThe period of the role. valid_from after valid_to is refused.
enters_contractbooleanOnly counts for the tenant role: the tenant enters the contract while the period covers the day.
statestringsuggested, confirmed or rejected. Default: confirmed
notestring | nullUp to 1000 characters.

PATCH /v1/parties/roles/{roleId} edits, confirms or rejects a role. Any edit by a caller is a decision: an edited suggestion becomes confirmed unless state says otherwise. A suggestion moved to another party or role leaves a rejected role for the original, so the next sync does not suggest it again. The body uses the same fields as the add route, all optional. The response is 200 with the role; 409 means the party already holds the requested role on the contract.

PATCH/v1/parties/roles/{roleId}

cURL: Give a tenant a role with a period

curl -X POST "https://api.talonic.com/v1/parties/rows/$PRODUCT_ID/$ROW_ID/roles" \
  -H "Authorization: Bearer $TALONIC_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "party_id": "e5a3c7b1-2d94-4f08-b6a2-81c0d9e47f35",
    "role": "tenant",
    "valid_from": "2025-04-01",
    "enters_contract": true
  }'

Response: A role with its evidence

{
  "id": "2f6d8b40-91c3-4e75-a0d2-5b7e1c3f9a86",
  "contract_id": null,
  "data_product_id": "3b9e5a14-7c20-4d6f-8a31-0f2d9c7e4b58",
  "row_id": "rec-10442",
  "party_id": "e5a3c7b1-2d94-4f08-b6a2-81c0d9e47f35",
  "role": "tenant",
  "valid_from": "2025-04-01",
  "valid_to": null,
  "enters_contract": true,
  "state": "confirmed",
  "confidence": null,
  "evidence": [{ "kind": "person", "by": "api:3c1e", "at": "2026-06-01T08:20:00.000Z", "note": null }],
  "origin": "person",
  "decided_by": "api:3c1e",
  "decided_at": "2026-06-01T08:20:00.000Z"
}
Confidence on a suggestion is low for an unchecked quote, medium for one source, and high when two sources name the same party. A role confirmed by a person is a decision and keeps its state and fields through every later fill.

Frequently asked questions

What is the difference between partner, effective_partner and addressee?+
partner is the party the contract is signed with. effective_partner is a tenant who enters the contract while the tenant's period covers the day, else the partner. addressee is whom a letter goes to: the effective partner's Verwalter when it has one, else the effective partner.
Does reading a row cost a model call?+
GET /v1/parties/rows/{productId}/{rowId} does not. POST .../read does: it queues one AI read of the row's documents. A read already pending or running is not queued again.
What does editing a suggested role do?+
Any edit by a caller is a decision, so an edited suggestion becomes confirmed unless the request sets state. If you move a suggestion to another party or role, the original is left as a rejected role so the next sync does not suggest it again.
Why does PATCH on a role return 409?+
The party already holds the requested role on the contract. Merge the roles by editing the existing one, or choose a different party or role.