Manage standard and custom Twenty records Use this guide to manage one standard or custom object through a current schema snapshot, bounded record discovery, duplicate review, one governed mutation, and a provider read that verifies the intended result. The connector applies the same outer object grammar to standard and custom objects. Twenty owns which objects and fields exist in the configured workspace and which fields are writable at the observed provider revision. Prerequisites Require: • an admitted and healthy Twenty host for the intended tenant and workspace; • a caller authorized to discover and invoke the selected capabilities; • a trusted tenant approver for every mutation; • an owner for the application's object and field allowlist; • a durable store for schema, Action, approval, key, and provider evidence; • a data-handling policy for confidential and personal record content. Set only AIP client coordinates: export AIPURL='https://aip.example.com' export AIPTOKENFILE='/run/secrets/aip-native-token' export OBJECT='companies' The operator owns the provider origin, workspace UUID, and API token. Do not add them to record input. 1. Confirm the admitted surface Discover the capabilities needed for the intended path. A create path normally uses these operations: aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ capability list "$AIPURL" \ --capability-id cap:twenty:openapi.core \ --limit 1 aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ capability list "$AIPURL" \ --capability-id cap:twenty:record.list \ --limit 1 aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ capability list "$AIPURL" \ --capability-id cap:twenty:record.create \ --limit 1 Record the catalog revision and each contract and schema digest. Stop when an operation is absent; a syntactically valid ID cannot bypass admission. For updates or lifecycle changes, discover that exact capability before constructing the Action. 2. Capture the current workspace schema Read the core OpenAPI document with an empty input: export SCHEMAACTIONID='acttwentyrecordsschema001' aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action call "$AIPURL" \ cap:twenty:openapi.core \ --action-id "$SCHEMAACTIONID" \ --mode sync \ --input '{}' Retain the body under tenant-scoped access and calculate its digest outside the connector. Record the workspace, credential revision, timestamp, provider request ID, and connector artifact identity without retaining secret bytes. Confirm that OBJECT exists and record: • provider object name and label; • required and writable fields; • field types and relationship targets; • unique and duplicate-sensitive fields; • fields that trigger automation or notification; • read and write permissions of the fixed provider credential; • fields excluded from application input, output, logs, and evidence. The document is an observation, not a compatibility guarantee. Stop when the observed object contract differs from the application's reviewed contract. 3. Freeze an application object contract Create a versioned application record that binds: { "workspaceidentity": "deployment-owned", "object": "companies", "openapidigest": "sha256-of-observed-document", "allowedreadfields": ["id", "name"], "allowedwritefields": ["name"], "duplicatefields": ["name"], "maximumbulkrecords": 1 } This is application policy, not connector input. Keep its revision with every governed mutation. For a custom object, derive every listed field from the observed workspace schema. Do not copy a standard-object example into a custom object without reviewing types, required values, and relationships. 4. Read a bounded existing set Write record-list.json using the exact object: { "object": "companies", "query": { "limit": 25, "filter": "name[like]:AIP%", "depth": 0 } } Submit the read: export LISTACTIONID='acttwentyrecordslist001' aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action call "$AIPURL" \ cap:twenty:record.list \ --action-id "$LISTACTIONID" \ --mode sync \ --input @record-list.json Validate that returned fields, object identity, pagination, and result size match the frozen application contract. Apply output minimization before storing or displaying provider records. 5. Review duplicate candidates Before create, write one candidate body under data: { "object": "companies", "body": { "data": [ { "name": "AIP Managed Company" } ] }, "query": { "depth": 0 } } Invoke cap:twenty:record.findduplicates with a new read Action. Review the provider candidates against the application's duplicate fields and current business ownership. Duplicate detection provides candidates. It does not select a survivor, authorize merge, or prove that no duplicate exists. Choose one path: | Observation | Next intent | | No acceptable match | Create one deterministic record | | One record is the intended entity | Update that exact ID when a change is needed | | Several records need consolidation | Stop and prepare a separately approved merge | | Schema or ownership is unclear | Stop and refresh the object contract | 6. Freeze one mutation For create, write the provider body once: { "object": "companies", "body": { "name": "AIP Managed Company" }, "query": { "depth": 0 } } For update, use the validated provider ID and only approved writable fields: { "object": "companies", "id": "0190c42f-2d5a-7000-8000-000000000060", "body": { "name": "AIP Managed Company Updated" }, "query": { "depth": 0 } } Freeze capability, input bytes, schema digest, object-policy revision, target ID, expected prior values, and expected result. A semantic change requires a new intent. 7. Submit one governed mutation Reserve stable identities for the selected path: export MUTATIONCAPABILITY='cap:twenty:record.create' export MUTATIONINPUT='record-create.json' export MUTATIONACTIONID='acttwentyrecordscreate001' export MUTATIONKEY='twenty-records-create-v1' Submit once: aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action call "$AIPURL" \ "$MUTATIONCAPABILITY" \ --action-id "$MUTATIONACTIONID" \ --idempotency-key "$MUTATIONKEY" \ --mode sync \ --input @"$MUTATIONINPUT" Without admitted authorization, the Action enters requireshuman. The trusted approver reviews the frozen input, schema and policy identities, duplicate evidence, target, consequences, and required reason. Approval resumes the same queued Action. Do not submit a replacement Action or change the key while the original remains pending. Create forces provider upsert and derives a missing record ID from workspace, key, and index zero. Update preserves the supplied target ID. Both advertise retry safety only with the original key and unchanged canonical input. 8. Read the durable mutation result Read the original Action: aipctl \ --native-bearer-token-file "$AIPTOKENFILE" \ action status "$AIPURL" \ "$MUTATIONACTIONID" \ --include-result \ --include-receipts \ --wait-ms 30000 Proceed only after terminal completed. Retain provider HTTP status, provider request ID when present, decoded body, approval, and receipts. A completed response does not prove that provider automation, webhooks, notifications, indexes, or integrations have completed. 9. Verify the provider record Extract a validated provider record ID from the result. Never derive it from an Action ID or assume the application name is unique. Write record-get.json: { "object": "companies", "id": "0190c42f-2d5a-7000-8000-000000000060", "query": { "depth": 0 } } Invoke cap:twenty:record.get under a new read Action. Compare only fields owned by the frozen application contract and retain the verification timestamp and provider request identity. If the result differs, do not overwrite it automatically. Classify schema drift, provider validation, automation, concurrency, wrong target, or uncertain prior execution before preparing another mutation. 10. Choose a separate lifecycle intent Use a new Action, key, approval, and current preflight for every lifecycle change. | Intent | Capability | Boundary | | Hide one recoverable record | record.softdelete | Restore depends on provider-retained state | | Restore one soft-deleted record | record.restore | Uses a filtered collection route at this revision | | Permanently remove one record | record.destroy | Critical; no connector rollback | | Consolidate duplicates | record.merge | Two through nine unique IDs; provider chooses merge semantics | | Change a bounded set | A many operation | Explicit non-empty filter and target evidence required | Do not treat soft delete and restore as one transaction. Do not use permanent destroy as duplicate cleanup without retention and dependency review. Recover without duplicating work | Observation | Response | | Action is requireshuman | Complete the existing approval workflow | | Action remains pending | Read the same Action; do not replace it | | Idempotency collision | Stop; the key owns different canonical input | | Definitive provider rejection | Refresh schema and state before a new intent | | Retryable create or single-update failure | Reuse only the original Action and key according to runtime policy | | Uncertain mutation outcome | Preserve all identities and inspect provider state before another write | | Provider record cannot be identified | Stop; do not guess an ID or repeat create | The connector exposes no reconciliation capability. Bounded provider reads and retained evidence must establish current state before a separately authorized recovery mutation. Retain the management evidence Keep: • catalog revision and selected capability digests; • connector artifact, provider mapping, tenant, workspace, and credential revisions; • observed OpenAPI body digest and application object-policy revision; • bounded list and duplicate Actions and their minimized results; • frozen mutation input and expected prior and resulting values; • Action ID, idempotency key, approval, policy hash, result, and receipts; • provider request and validated record IDs; • post-mutation read and comparison; • any lifecycle or recovery Action as a separate evidence chain. This evidence demonstrates one controlled record workflow. It does not by itself establish live-provider qualification or general compatibility for every standard or custom object. Related documentation • Twenty OpenAPI documents (../capabilities/openapi-documents.md) • Record query and aggregation (../capabilities/record-query-and-aggregation.md) • Record create, update, and batch upsert (../capabilities/record-create-update-and-batch-upsert.md) • Record delete, restore, and merge (../capabilities/record-delete-restore-and-merge.md) • Twenty connector quickstart (../getting-started/quickstart.md)