返回 Skill 列表
extension
分类: 其它需要 API Key

Twenty CRM

Twenty CRM API 集成,托管身份验证,可管理公司、人员、机会、笔记和任务,适用于用户交互需求。

person作者: byungkyuhubclawhub

Twenty CRM

Access the Twenty CRM API with managed authentication. Manage companies, people, opportunities, notes, tasks, and workflows.

Quick Start

# List companies
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/twenty/rest/companies?limit=10')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Base URL

https://api.maton.ai/twenty/rest/

Only the endpoints listed in the API Reference section below are supported. Maton proxies requests to api.twenty.com/rest/ and automatically injects your API token.

Authentication

All requests require the Maton API key in the Authorization header:

Authorization: Bearer $MATON_API_KEY

Environment Variable: Set your API key as MATON_API_KEY:

export MATON_API_KEY="YOUR_API_KEY"

Getting Your API Key

  1. Sign in or create an account at maton.ai
  2. Go to maton.ai/settings
  3. Copy your API key

Connection Management

Manage your Twenty connections at https://api.maton.ai.

List Connections

python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections?app=twenty&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Create Connection

python <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'twenty'}).encode()
req = urllib.request.Request('https://api.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Get Connection

python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Response:

{
  "connection": {
    "connection_id": "{connection_id}",
    "status": "ACTIVE",
    "creation_time": "2025-12-08T07:20:53.488460Z",
    "last_updated_time": "2026-01-31T20:03:32.593153Z",
    "url": "https://connect.maton.ai/?session_token=...",
    "app": "twenty",
    "metadata": {}
  }
}

Open the returned url in a browser to complete authorization.

Delete Connection

python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

Specifying Connection

If you have multiple Twenty connections, specify which one to use with the Maton-Connection header:

python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/twenty/rest/companies?limit=10')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '{connection_id}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF

If you have multiple connections, always include this header to ensure requests go to the intended account.

Security & Permissions

  • Access is scoped to companies, people, opportunities, notes, tasks, and workflows within the connected Twenty CRM.
  • All write operations require explicit user approval. Before executing any create, update, or delete call, confirm the exact record ID and intended effect with the user.
  • CRM data scope: Mutations directly affect live CRM records and workflows shared across the workspace. Prefer list/get calls to verify target records before making changes.

API Reference

Companies

List Companies

GET /twenty/rest/companies?limit=20

Response:

{
  "data": {
    "companies": [
      {
        "id": "06290608-8bf0-4806-99ae-a715a6a93fad",
        "name": "Acme Corp",
        "domainName": {
          "primaryLinkUrl": "https://acme.com"
        },
        "employees": 100,
        "address": {
          "addressCity": "San Francisco",
          "addressState": "CA",
          "addressCountry": "United States"
        },
        "createdAt": "2026-03-20T23:59:52.906Z",
        "updatedAt": "2026-03-20T23:59:52.906Z"
      }
    ]
  },
  "pageInfo": {
    "hasNextPage": true,
    "startCursor": "06290608-8bf0-4806-99ae-a715a6a93fad",
    "endCursor": "1f70157c-4ea5-4d81-bc49-e1401abfbb94"
  },
  "totalCount": 50
}

Get Company

GET /twenty/rest/companies/{id}

Create Company

POST /twenty/rest/companies
Content-Type: application/json

{
  "name": "New Company",
  "domainName": {
    "primaryLinkUrl": "https://newcompany.com"
  },
  "employees": 50
}

Update Company

PATCH /twenty/rest/companies/{id}
Content-Type: application/json

{
  "name": "Updated Company Name",
  "employees": 100
}

Delete Company

DELETE /twenty/rest/companies/{id}

People

List People

GET /twenty/rest/people?limit=20

Response:

{
  "data": {
    "people": [
      {
        "id": "7a93d1e5-3f74-4945-8a65-d7f996083f72",
        "name": {
          "firstName": "John",
          "lastName": "Doe"
        },
        "emails": {
          "primaryEmail": "john@company.com"
        },
        "phones": {
          "primaryPhoneNumber": "5551234567",
          "primaryPhoneCallingCode": "+1"
        },
        "jobTitle": "CEO",
        "city": "San Francisco",
        "companyId": "06290608-8bf0-4806-99ae-a715a6a93fad"
      }
    ]
  },
  "pageInfo": {...},
  "totalCount": 100
}

Get Person

GET /twenty/rest/people/{id}

Create Person

POST /twenty/rest/people
Content-Type: application/json

{
  "name": {
    "firstName": "Jane",
    "lastName": "Smith"
  },
  "emails": {
    "primaryEmail": "jane@company.com"
  },
  "jobTitle": "CTO",
  "companyId": "06290608-8bf0-4806-99ae-a715a6a93fad"
}

Update Person

PATCH /twenty/rest/people/{id}
Content-Type: application/json

{
  "jobTitle": "VP of Engineering"
}

Delete Person

DELETE /twenty/rest/people/{id}

Opportunities

List Opportunities

GET /twenty/rest/opportunities?limit=20

Response:

{
  "data": {
    "opportunities": [
      {
        "id": "2beb07b0-340c-41d7-be33-5aa91757f329",
        "name": "Enterprise Deal",
        "amount": {
          "amountMicros": 75000000000,
          "currencyCode": "USD"
        },
        "closeDate": "2026-01-25T16:26:00.000Z",
        "stage": "SCREENING",
        "companyId": "1f70157c-4ea5-4d81-bc49-e1401abfbb94",
        "pointOfContactId": "edf6d445-13a7-4373-9a47-8f89e8c0a877"
      }
    ]
  },
  "pageInfo": {...},
  "totalCount": 25
}

Note: Amount is stored in micros (divide by 1,000,000 for actual value).

Get Opportunity

GET /twenty/rest/opportunities/{id}

Create Opportunity

POST /twenty/rest/opportunities
Content-Type: application/json

{
  "name": "New Deal",
  "amount": {
    "amountMicros": 50000000000,
    "currencyCode": "USD"
  },
  "stage": "SCREENING",
  "closeDate": "2026-06-01T00:00:00.000Z",
  "companyId": "06290608-8bf0-4806-99ae-a715a6a93fad"
}

Update Opportunity

PATCH /twenty/rest/opportunities/{id}
Content-Type: application/json

{
  "stage": "MEETING",
  "amount": {
    "amountMicros": 60000000000,
    "currencyCode": "USD"
  }
}

Delete Opportunity

DELETE /twenty/rest/opportunities/{id}

Notes

List Notes

GET /twenty/rest/notes?limit=20

Get Note

GET /twenty/rest/notes/{id}

Create Note

POST /twenty/rest/notes
Content-Type: application/json

{
  "title": "Meeting Notes",
  "body": "Discussed Q2 roadmap and partnership opportunities."
}

Update Note

PATCH /twenty/rest/notes/{id}
Content-Type: application/json

{
  "body": "Updated meeting notes with action items."
}

Delete Note

DELETE /twenty/rest/notes/{id}

Tasks

List Tasks

GET /twenty/rest/tasks?limit=20

Get Task

GET /twenty/rest/tasks/{id}

Create Task

POST /twenty/rest/tasks
Content-Type: application/json

{
  "title": "Follow up with client",
  "body": "Send proposal and schedule demo",
  "dueAt": "2026-04-01T00:00:00.000Z",
  "status": "TODO"
}

Update Task

PATCH /twenty/rest/tasks/{id}
Content-Type: application/json

{
  "status": "DONE"
}

Delete Task

DELETE /twenty/rest/tasks/{id}

Workspace Members

List Workspace Members

GET /twenty/rest/workspaceMembers?limit=20

Filtering

Use the filter query parameter to narrow results:

GET /twenty/rest/companies?filter=employees[gte]:100
GET /twenty/rest/opportunities?filter=stage[eq]:"MEETING"
GET /twenty/rest/people?filter=name.firstName[ilike]:"%john%"

Comparators:

  • eq, neq - Equal, not equal
  • gt, gte, lt, lte - Greater/less than
  • in - In array: id[in]:["id-1","id-2"]
  • is - Null check: deletedAt[is]:NULL
  • like, ilike - Pattern match (case-sensitive/insensitive)
  • startsWith - Prefix match
  • contain, notContain - Contains value

Advanced filtering:

filter=or(stage[eq]:"MEETING",stage[eq]:"SCREENING")
filter=and(employees[gte]:100,idealCustomerProfile[eq]:true)

Pagination

Twenty uses cursor-based pagination:

GET /twenty/rest/companies?limit=20&starting_after={endCursor}

Parameters:

  • limit - Results per page (default: 60, max: 60)
  • starting_after - Cursor for next page (use endCursor from response)
  • ending_before - Cursor for previous page (use startCursor from response)

Response includes:

{
  "pageInfo": {
    "hasNextPage": true,
    "hasPreviousPage": false,
    "startCursor": "uuid-1",
    "endCursor": "uuid-2"
  },
  "totalCount": 150
}

Ordering

Use order_by to sort results:

GET /twenty/rest/companies?order_by=createdAt[DescNullsLast]
GET /twenty/rest/opportunities?order_by=closeDate,amount[DescNullsFirst]

Directions: AscNullsFirst, AscNullsLast, DescNullsFirst, DescNullsLast

Code Examples

JavaScript

const response = await fetch(
  'https://api.maton.ai/twenty/rest/companies?limit=10',
  {
    headers: {
      'Authorization': `Bearer ${process.env.MATON_API_KEY}`
    }
  }
);
const data = await response.json();

Python

import os
import requests

response = requests.get(
    'https://api.maton.ai/twenty/rest/companies',
    headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
    params={'limit': 10}
)
data = response.json()

Create Company

import os
import requests

response = requests.post(
    'https://api.maton.ai/twenty/rest/companies',
    headers={
        'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
        'Content-Type': 'application/json'
    },
    json={
        'name': 'New Company',
        'domainName': {'primaryLinkUrl': 'https://newcompany.com'},
        'employees': 50
    }
)

Notes

  • All IDs are UUIDs
  • Timestamps are in ISO 8601 format
  • Amount fields use micros (multiply by 1,000,000)
  • Opportunity stages: SCREENING, MEETING, PROPOSAL, NEGOTIATION, WON, LOST
  • Task statuses: TODO, IN_PROGRESS, DONE
  • IMPORTANT: When using curl commands, use curl -g when URLs contain brackets to disable glob parsing
  • IMPORTANT: When piping curl output to jq, environment variables may not expand correctly in some shells

Error Handling

| Status | Meaning | |--------|---------| | 400 | Missing Twenty connection or invalid request | | 401 | Invalid or missing Maton API key | | 404 | Resource not found | | 429 | Rate limited | | 4xx/5xx | Passthrough error from Twenty API |

Resources