{
  "schemaVersion": "1.0",
  "title": "Manage standard and custom Twenty records",
  "description": "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.",
  "canonical": "https://getaip.org/docs/connectors/twenty/guides/manage-standard-and-custom-records",
  "route": "/docs/connectors/twenty/guides/manage-standard-and-custom-records",
  "source": "docs/connectors/twenty/guides/manage-standard-and-custom-records.md",
  "protocol": "Agent Interoperability Protocol",
  "protocolVersion": "1.0",
  "section": "Connectors",
  "documentType": "Connector",
  "language": "en",
  "downloads": {
    "md": "/docs/download/connectors/twenty/guides/manage-standard-and-custom-records.md",
    "txt": "/docs/download/connectors/twenty/guides/manage-standard-and-custom-records.txt",
    "json": "/docs/download/connectors/twenty/guides/manage-standard-and-custom-records.json",
    "pdf": "/docs/download/connectors/twenty/guides/manage-standard-and-custom-records.pdf"
  },
  "content": {
    "format": "text/markdown",
    "markdown": "---\ntitle: Manage standard and custom Twenty records\ndescription: >-\n  Capture the workspace schema, find or create one record, verify the mutation,\n  and choose a safe follow-up lifecycle action\nkind: how-to\naudience: application-developer\nappliesTo: \"1.x\"\nwritingStandard: \"aip-docs/1.0\"\nlastReviewedRevision: \"97be86e9efedf07ecf1783b03800f683f107fb04\"\nconnector: twenty\n---\n\n# Manage standard and custom Twenty records\n\nUse this guide to manage one standard or custom object through a current schema\nsnapshot, bounded record discovery, duplicate review, one governed mutation,\nand a provider read that verifies the intended result.\n\nThe connector applies the same outer object grammar to standard and custom\nobjects. Twenty owns which objects and fields exist in the configured workspace\nand which fields are writable at the observed provider revision.\n\n## Prerequisites\n\nRequire:\n\n- an admitted and healthy Twenty host for the intended tenant and workspace;\n- a caller authorized to discover and invoke the selected capabilities;\n- a trusted tenant approver for every mutation;\n- an owner for the application's object and field allowlist;\n- a durable store for schema, Action, approval, key, and provider evidence;\n- a data-handling policy for confidential and personal record content.\n\nSet only AIP client coordinates:\n\n```sh\nexport AIP_URL='https://aip.example.com'\nexport AIP_TOKEN_FILE='/run/secrets/aip-native-token'\nexport OBJECT='companies'\n```\n\nThe operator owns the provider origin, workspace UUID, and API token. Do not\nadd them to record input.\n\n## 1. Confirm the admitted surface\n\nDiscover the capabilities needed for the intended path. A create path normally\nuses these operations:\n\n```sh\naipctl \\\n  --native-bearer-token-file \"$AIP_TOKEN_FILE\" \\\n  capability list \"$AIP_URL\" \\\n  --capability-id cap:twenty:openapi.core \\\n  --limit 1\n\naipctl \\\n  --native-bearer-token-file \"$AIP_TOKEN_FILE\" \\\n  capability list \"$AIP_URL\" \\\n  --capability-id cap:twenty:record.list \\\n  --limit 1\n\naipctl \\\n  --native-bearer-token-file \"$AIP_TOKEN_FILE\" \\\n  capability list \"$AIP_URL\" \\\n  --capability-id cap:twenty:record.create \\\n  --limit 1\n```\n\nRecord the catalog revision and each contract and schema digest. Stop when an\noperation is absent; a syntactically valid ID cannot bypass admission.\n\nFor updates or lifecycle changes, discover that exact capability before\nconstructing the Action.\n\n## 2. Capture the current workspace schema\n\nRead the core OpenAPI document with an empty input:\n\n```sh\nexport SCHEMA_ACTION_ID='act_twenty_records_schema_001'\n\naipctl \\\n  --native-bearer-token-file \"$AIP_TOKEN_FILE\" \\\n  action call \"$AIP_URL\" \\\n  cap:twenty:openapi.core \\\n  --action-id \"$SCHEMA_ACTION_ID\" \\\n  --mode sync \\\n  --input '{}'\n```\n\nRetain the body under tenant-scoped access and calculate its digest outside the\nconnector. Record the workspace, credential revision, timestamp, provider\nrequest ID, and connector artifact identity without retaining secret bytes.\n\nConfirm that `OBJECT` exists and record:\n\n- provider object name and label;\n- required and writable fields;\n- field types and relationship targets;\n- unique and duplicate-sensitive fields;\n- fields that trigger automation or notification;\n- read and write permissions of the fixed provider credential;\n- fields excluded from application input, output, logs, and evidence.\n\nThe document is an observation, not a compatibility guarantee. Stop when the\nobserved object contract differs from the application's reviewed contract.\n\n## 3. Freeze an application object contract\n\nCreate a versioned application record that binds:\n\n```json\n{\n  \"workspace_identity\": \"deployment-owned\",\n  \"object\": \"companies\",\n  \"openapi_digest\": \"sha256-of-observed-document\",\n  \"allowed_read_fields\": [\"id\", \"name\"],\n  \"allowed_write_fields\": [\"name\"],\n  \"duplicate_fields\": [\"name\"],\n  \"maximum_bulk_records\": 1\n}\n```\n\nThis is application policy, not connector input. Keep its revision with every\ngoverned mutation.\n\nFor a custom object, derive every listed field from the observed workspace\nschema. Do not copy a standard-object example into a custom object without\nreviewing types, required values, and relationships.\n\n## 4. Read a bounded existing set\n\nWrite `record-list.json` using the exact object:\n\n```json\n{\n  \"object\": \"companies\",\n  \"query\": {\n    \"limit\": 25,\n    \"filter\": \"name[like]:AIP%\",\n    \"depth\": 0\n  }\n}\n```\n\nSubmit the read:\n\n```sh\nexport LIST_ACTION_ID='act_twenty_records_list_001'\n\naipctl \\\n  --native-bearer-token-file \"$AIP_TOKEN_FILE\" \\\n  action call \"$AIP_URL\" \\\n  cap:twenty:record.list \\\n  --action-id \"$LIST_ACTION_ID\" \\\n  --mode sync \\\n  --input @record-list.json\n```\n\nValidate that returned fields, object identity, pagination, and result size\nmatch the frozen application contract. Apply output minimization before storing\nor displaying provider records.\n\n## 5. Review duplicate candidates\n\nBefore create, write one candidate body under `data`:\n\n```json\n{\n  \"object\": \"companies\",\n  \"body\": {\n    \"data\": [\n      {\n        \"name\": \"AIP Managed Company\"\n      }\n    ]\n  },\n  \"query\": {\n    \"depth\": 0\n  }\n}\n```\n\nInvoke `cap:twenty:record.find_duplicates` with a new read Action. Review the\nprovider candidates against the application's duplicate fields and current\nbusiness ownership.\n\nDuplicate detection provides candidates. It does not select a survivor,\nauthorize merge, or prove that no duplicate exists.\n\nChoose one path:\n\n| Observation | Next intent |\n|---|---|\n| No acceptable match | Create one deterministic record |\n| One record is the intended entity | Update that exact ID when a change is needed |\n| Several records need consolidation | Stop and prepare a separately approved merge |\n| Schema or ownership is unclear | Stop and refresh the object contract |\n\n## 6. Freeze one mutation\n\nFor create, write the provider body once:\n\n```json\n{\n  \"object\": \"companies\",\n  \"body\": {\n    \"name\": \"AIP Managed Company\"\n  },\n  \"query\": {\n    \"depth\": 0\n  }\n}\n```\n\nFor update, use the validated provider ID and only approved writable fields:\n\n```json\n{\n  \"object\": \"companies\",\n  \"id\": \"0190c42f-2d5a-7000-8000-000000000060\",\n  \"body\": {\n    \"name\": \"AIP Managed Company Updated\"\n  },\n  \"query\": {\n    \"depth\": 0\n  }\n}\n```\n\nFreeze capability, input bytes, schema digest, object-policy revision, target\nID, expected prior values, and expected result. A semantic change requires a\nnew intent.\n\n## 7. Submit one governed mutation\n\nReserve stable identities for the selected path:\n\n```sh\nexport MUTATION_CAPABILITY='cap:twenty:record.create'\nexport MUTATION_INPUT='record-create.json'\nexport MUTATION_ACTION_ID='act_twenty_records_create_001'\nexport MUTATION_KEY='twenty-records-create-v1'\n```\n\nSubmit once:\n\n```sh\naipctl \\\n  --native-bearer-token-file \"$AIP_TOKEN_FILE\" \\\n  action call \"$AIP_URL\" \\\n  \"$MUTATION_CAPABILITY\" \\\n  --action-id \"$MUTATION_ACTION_ID\" \\\n  --idempotency-key \"$MUTATION_KEY\" \\\n  --mode sync \\\n  --input @\"$MUTATION_INPUT\"\n```\n\nWithout admitted authorization, the Action enters `requires_human`. The trusted\napprover reviews the frozen input, schema and policy identities, duplicate\nevidence, target, consequences, and required reason.\n\nApproval resumes the same queued Action. Do not submit a replacement Action or\nchange the key while the original remains pending.\n\nCreate forces provider upsert and derives a missing record ID from workspace,\nkey, and index zero. Update preserves the supplied target ID. Both advertise\nretry safety only with the original key and unchanged canonical input.\n\n## 8. Read the durable mutation result\n\nRead the original Action:\n\n```sh\naipctl \\\n  --native-bearer-token-file \"$AIP_TOKEN_FILE\" \\\n  action status \"$AIP_URL\" \\\n  \"$MUTATION_ACTION_ID\" \\\n  --include-result \\\n  --include-receipts \\\n  --wait-ms 30000\n```\n\nProceed only after terminal `completed`. Retain provider HTTP status, provider\nrequest ID when present, decoded body, approval, and receipts.\n\nA completed response does not prove that provider automation, webhooks,\nnotifications, indexes, or integrations have completed.\n\n## 9. Verify the provider record\n\nExtract a validated provider record ID from the result. Never derive it from an\nAction ID or assume the application name is unique.\n\nWrite `record-get.json`:\n\n```json\n{\n  \"object\": \"companies\",\n  \"id\": \"0190c42f-2d5a-7000-8000-000000000060\",\n  \"query\": {\n    \"depth\": 0\n  }\n}\n```\n\nInvoke `cap:twenty:record.get` under a new read Action. Compare only fields\nowned by the frozen application contract and retain the verification timestamp\nand provider request identity.\n\nIf the result differs, do not overwrite it automatically. Classify schema\ndrift, provider validation, automation, concurrency, wrong target, or uncertain\nprior execution before preparing another mutation.\n\n## 10. Choose a separate lifecycle intent\n\nUse a new Action, key, approval, and current preflight for every lifecycle\nchange.\n\n| Intent | Capability | Boundary |\n|---|---|---|\n| Hide one recoverable record | `record.soft_delete` | Restore depends on provider-retained state |\n| Restore one soft-deleted record | `record.restore` | Uses a filtered collection route at this revision |\n| Permanently remove one record | `record.destroy` | Critical; no connector rollback |\n| Consolidate duplicates | `record.merge` | Two through nine unique IDs; provider chooses merge semantics |\n| Change a bounded set | A `*_many` operation | Explicit non-empty filter and target evidence required |\n\nDo not treat soft delete and restore as one transaction. Do not use permanent\ndestroy as duplicate cleanup without retention and dependency review.\n\n## Recover without duplicating work\n\n| Observation | Response |\n|---|---|\n| Action is `requires_human` | Complete the existing approval workflow |\n| Action remains pending | Read the same Action; do not replace it |\n| Idempotency collision | Stop; the key owns different canonical input |\n| Definitive provider rejection | Refresh schema and state before a new intent |\n| Retryable create or single-update failure | Reuse only the original Action and key according to runtime policy |\n| Uncertain mutation outcome | Preserve all identities and inspect provider state before another write |\n| Provider record cannot be identified | Stop; do not guess an ID or repeat create |\n\nThe connector exposes no reconciliation capability. Bounded provider reads and\nretained evidence must establish current state before a separately authorized\nrecovery mutation.\n\n## Retain the management evidence\n\nKeep:\n\n- catalog revision and selected capability digests;\n- connector artifact, provider mapping, tenant, workspace, and credential revisions;\n- observed OpenAPI body digest and application object-policy revision;\n- bounded list and duplicate Actions and their minimized results;\n- frozen mutation input and expected prior and resulting values;\n- Action ID, idempotency key, approval, policy hash, result, and receipts;\n- provider request and validated record IDs;\n- post-mutation read and comparison;\n- any lifecycle or recovery Action as a separate evidence chain.\n\nThis evidence demonstrates one controlled record workflow. It does not by\nitself establish live-provider qualification or general compatibility for\nevery standard or custom object.\n\n## Related documentation\n\n- [Twenty OpenAPI documents](../capabilities/openapi-documents.md)\n- [Record query and aggregation](../capabilities/record-query-and-aggregation.md)\n- [Record create, update, and batch upsert](../capabilities/record-create-update-and-batch-upsert.md)\n- [Record delete, restore, and merge](../capabilities/record-delete-restore-and-merge.md)\n- [Twenty connector quickstart](../getting-started/quickstart.md)\n",
    "text": "Manage standard and custom Twenty records\n\nUse this guide to manage one standard or custom object through a current schema\nsnapshot, bounded record discovery, duplicate review, one governed mutation,\nand a provider read that verifies the intended result.\n\nThe connector applies the same outer object grammar to standard and custom\nobjects. Twenty owns which objects and fields exist in the configured workspace\nand which fields are writable at the observed provider revision.\n\nPrerequisites\n\nRequire:\n• an admitted and healthy Twenty host for the intended tenant and workspace;\n• a caller authorized to discover and invoke the selected capabilities;\n• a trusted tenant approver for every mutation;\n• an owner for the application's object and field allowlist;\n• a durable store for schema, Action, approval, key, and provider evidence;\n• a data-handling policy for confidential and personal record content.\n\nSet only AIP client coordinates:\n\nexport AIPURL='https://aip.example.com'\nexport AIPTOKENFILE='/run/secrets/aip-native-token'\nexport OBJECT='companies'\n\nThe operator owns the provider origin, workspace UUID, and API token. Do not\nadd them to record input.\n1. Confirm the admitted surface\n\nDiscover the capabilities needed for the intended path. A create path normally\nuses these operations:\n\naipctl \\\n  --native-bearer-token-file \"$AIPTOKENFILE\" \\\n  capability list \"$AIPURL\" \\\n  --capability-id cap:twenty:openapi.core \\\n  --limit 1\n\naipctl \\\n  --native-bearer-token-file \"$AIPTOKENFILE\" \\\n  capability list \"$AIPURL\" \\\n  --capability-id cap:twenty:record.list \\\n  --limit 1\n\naipctl \\\n  --native-bearer-token-file \"$AIPTOKENFILE\" \\\n  capability list \"$AIPURL\" \\\n  --capability-id cap:twenty:record.create \\\n  --limit 1\n\nRecord the catalog revision and each contract and schema digest. Stop when an\noperation is absent; a syntactically valid ID cannot bypass admission.\n\nFor updates or lifecycle changes, discover that exact capability before\nconstructing the Action.\n2. Capture the current workspace schema\n\nRead the core OpenAPI document with an empty input:\n\nexport SCHEMAACTIONID='acttwentyrecordsschema001'\n\naipctl \\\n  --native-bearer-token-file \"$AIPTOKENFILE\" \\\n  action call \"$AIPURL\" \\\n  cap:twenty:openapi.core \\\n  --action-id \"$SCHEMAACTIONID\" \\\n  --mode sync \\\n  --input '{}'\n\nRetain the body under tenant-scoped access and calculate its digest outside the\nconnector. Record the workspace, credential revision, timestamp, provider\nrequest ID, and connector artifact identity without retaining secret bytes.\n\nConfirm that OBJECT exists and record:\n• provider object name and label;\n• required and writable fields;\n• field types and relationship targets;\n• unique and duplicate-sensitive fields;\n• fields that trigger automation or notification;\n• read and write permissions of the fixed provider credential;\n• fields excluded from application input, output, logs, and evidence.\n\nThe document is an observation, not a compatibility guarantee. Stop when the\nobserved object contract differs from the application's reviewed contract.\n3. Freeze an application object contract\n\nCreate a versioned application record that binds:\n\n{\n  \"workspaceidentity\": \"deployment-owned\",\n  \"object\": \"companies\",\n  \"openapidigest\": \"sha256-of-observed-document\",\n  \"allowedreadfields\": [\"id\", \"name\"],\n  \"allowedwritefields\": [\"name\"],\n  \"duplicatefields\": [\"name\"],\n  \"maximumbulkrecords\": 1\n}\n\nThis is application policy, not connector input. Keep its revision with every\ngoverned mutation.\n\nFor a custom object, derive every listed field from the observed workspace\nschema. Do not copy a standard-object example into a custom object without\nreviewing types, required values, and relationships.\n4. Read a bounded existing set\n\nWrite record-list.json using the exact object:\n\n{\n  \"object\": \"companies\",\n  \"query\": {\n    \"limit\": 25,\n    \"filter\": \"name[like]:AIP%\",\n    \"depth\": 0\n  }\n}\n\nSubmit the read:\n\nexport LISTACTIONID='acttwentyrecordslist001'\n\naipctl \\\n  --native-bearer-token-file \"$AIPTOKENFILE\" \\\n  action call \"$AIPURL\" \\\n  cap:twenty:record.list \\\n  --action-id \"$LISTACTIONID\" \\\n  --mode sync \\\n  --input @record-list.json\n\nValidate that returned fields, object identity, pagination, and result size\nmatch the frozen application contract. Apply output minimization before storing\nor displaying provider records.\n5. Review duplicate candidates\n\nBefore create, write one candidate body under data:\n\n{\n  \"object\": \"companies\",\n  \"body\": {\n    \"data\": [\n      {\n        \"name\": \"AIP Managed Company\"\n      }\n    ]\n  },\n  \"query\": {\n    \"depth\": 0\n  }\n}\n\nInvoke cap:twenty:record.findduplicates with a new read Action. Review the\nprovider candidates against the application's duplicate fields and current\nbusiness ownership.\n\nDuplicate detection provides candidates. It does not select a survivor,\nauthorize merge, or prove that no duplicate exists.\n\nChoose one path:\n\n| Observation | Next intent |\n\n| No acceptable match | Create one deterministic record |\n| One record is the intended entity | Update that exact ID when a change is needed |\n| Several records need consolidation | Stop and prepare a separately approved merge |\n| Schema or ownership is unclear | Stop and refresh the object contract |\n6. Freeze one mutation\n\nFor create, write the provider body once:\n\n{\n  \"object\": \"companies\",\n  \"body\": {\n    \"name\": \"AIP Managed Company\"\n  },\n  \"query\": {\n    \"depth\": 0\n  }\n}\n\nFor update, use the validated provider ID and only approved writable fields:\n\n{\n  \"object\": \"companies\",\n  \"id\": \"0190c42f-2d5a-7000-8000-000000000060\",\n  \"body\": {\n    \"name\": \"AIP Managed Company Updated\"\n  },\n  \"query\": {\n    \"depth\": 0\n  }\n}\n\nFreeze capability, input bytes, schema digest, object-policy revision, target\nID, expected prior values, and expected result. A semantic change requires a\nnew intent.\n7. Submit one governed mutation\n\nReserve stable identities for the selected path:\n\nexport MUTATIONCAPABILITY='cap:twenty:record.create'\nexport MUTATIONINPUT='record-create.json'\nexport MUTATIONACTIONID='acttwentyrecordscreate001'\nexport MUTATIONKEY='twenty-records-create-v1'\n\nSubmit once:\n\naipctl \\\n  --native-bearer-token-file \"$AIPTOKENFILE\" \\\n  action call \"$AIPURL\" \\\n  \"$MUTATIONCAPABILITY\" \\\n  --action-id \"$MUTATIONACTIONID\" \\\n  --idempotency-key \"$MUTATIONKEY\" \\\n  --mode sync \\\n  --input @\"$MUTATIONINPUT\"\n\nWithout admitted authorization, the Action enters requireshuman. The trusted\napprover reviews the frozen input, schema and policy identities, duplicate\nevidence, target, consequences, and required reason.\n\nApproval resumes the same queued Action. Do not submit a replacement Action or\nchange the key while the original remains pending.\n\nCreate forces provider upsert and derives a missing record ID from workspace,\nkey, and index zero. Update preserves the supplied target ID. Both advertise\nretry safety only with the original key and unchanged canonical input.\n8. Read the durable mutation result\n\nRead the original Action:\n\naipctl \\\n  --native-bearer-token-file \"$AIPTOKENFILE\" \\\n  action status \"$AIPURL\" \\\n  \"$MUTATIONACTIONID\" \\\n  --include-result \\\n  --include-receipts \\\n  --wait-ms 30000\n\nProceed only after terminal completed. Retain provider HTTP status, provider\nrequest ID when present, decoded body, approval, and receipts.\n\nA completed response does not prove that provider automation, webhooks,\nnotifications, indexes, or integrations have completed.\n9. Verify the provider record\n\nExtract a validated provider record ID from the result. Never derive it from an\nAction ID or assume the application name is unique.\n\nWrite record-get.json:\n\n{\n  \"object\": \"companies\",\n  \"id\": \"0190c42f-2d5a-7000-8000-000000000060\",\n  \"query\": {\n    \"depth\": 0\n  }\n}\n\nInvoke cap:twenty:record.get under a new read Action. Compare only fields\nowned by the frozen application contract and retain the verification timestamp\nand provider request identity.\n\nIf the result differs, do not overwrite it automatically. Classify schema\ndrift, provider validation, automation, concurrency, wrong target, or uncertain\nprior execution before preparing another mutation.\n10. Choose a separate lifecycle intent\n\nUse a new Action, key, approval, and current preflight for every lifecycle\nchange.\n\n| Intent | Capability | Boundary |\n\n| Hide one recoverable record | record.softdelete | Restore depends on provider-retained state |\n| Restore one soft-deleted record | record.restore | Uses a filtered collection route at this revision |\n| Permanently remove one record | record.destroy | Critical; no connector rollback |\n| Consolidate duplicates | record.merge | Two through nine unique IDs; provider chooses merge semantics |\n| Change a bounded set | A many operation | Explicit non-empty filter and target evidence required |\n\nDo not treat soft delete and restore as one transaction. Do not use permanent\ndestroy as duplicate cleanup without retention and dependency review.\n\nRecover without duplicating work\n\n| Observation | Response |\n\n| Action is requireshuman | Complete the existing approval workflow |\n| Action remains pending | Read the same Action; do not replace it |\n| Idempotency collision | Stop; the key owns different canonical input |\n| Definitive provider rejection | Refresh schema and state before a new intent |\n| Retryable create or single-update failure | Reuse only the original Action and key according to runtime policy |\n| Uncertain mutation outcome | Preserve all identities and inspect provider state before another write |\n| Provider record cannot be identified | Stop; do not guess an ID or repeat create |\n\nThe connector exposes no reconciliation capability. Bounded provider reads and\nretained evidence must establish current state before a separately authorized\nrecovery mutation.\n\nRetain the management evidence\n\nKeep:\n• catalog revision and selected capability digests;\n• connector artifact, provider mapping, tenant, workspace, and credential revisions;\n• observed OpenAPI body digest and application object-policy revision;\n• bounded list and duplicate Actions and their minimized results;\n• frozen mutation input and expected prior and resulting values;\n• Action ID, idempotency key, approval, policy hash, result, and receipts;\n• provider request and validated record IDs;\n• post-mutation read and comparison;\n• any lifecycle or recovery Action as a separate evidence chain.\n\nThis evidence demonstrates one controlled record workflow. It does not by\nitself establish live-provider qualification or general compatibility for\nevery standard or custom object.\n\nRelated documentation\n• Twenty OpenAPI documents (../capabilities/openapi-documents.md)\n• Record query and aggregation (../capabilities/record-query-and-aggregation.md)\n• Record create, update, and batch upsert (../capabilities/record-create-update-and-batch-upsert.md)\n• Record delete, restore, and merge (../capabilities/record-delete-restore-and-merge.md)\n• Twenty connector quickstart (../getting-started/quickstart.md)\n"
  },
  "integrity": {
    "algorithm": "sha256",
    "sourceDigest": "81c01a21efa9189ae9a34ffc68787715387ffd7e7b6b9af05b638c0cf34c52b4"
  }
}
