501 lines
13 KiB
Markdown
501 lines
13 KiB
Markdown
|
|
---
|
||
|
|
name: google-contacts
|
||
|
|
description: |
|
||
|
|
Google Contacts API integration with managed OAuth. Manage contacts, contact groups, and search your address book.
|
||
|
|
Use this skill when users want to create, read, update, or delete contacts, manage contact groups, or search for people in their Google account.
|
||
|
|
For other third party apps, use the api-gateway skill (https://clawhub.ai/byungkyu/api-gateway).
|
||
|
|
compatibility: Requires network access and valid Maton API key
|
||
|
|
metadata:
|
||
|
|
author: maton
|
||
|
|
version: "1.0"
|
||
|
|
clawdbot:
|
||
|
|
emoji: 🧠
|
||
|
|
requires:
|
||
|
|
env:
|
||
|
|
- MATON_API_KEY
|
||
|
|
---
|
||
|
|
|
||
|
|
# Google Contacts
|
||
|
|
|
||
|
|
Access the Google People API with managed OAuth authentication. Manage contacts, contact groups, and search your address book.
|
||
|
|
|
||
|
|
## Quick Start
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# List contacts
|
||
|
|
python <<'EOF'
|
||
|
|
import urllib.request, os, json
|
||
|
|
req = urllib.request.Request('https://gateway.maton.ai/google-contacts/v1/people/me/connections?personFields=names,emailAddresses,phoneNumbers&pageSize=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://gateway.maton.ai/google-contacts/{native-api-path}
|
||
|
|
```
|
||
|
|
|
||
|
|
Replace `{native-api-path}` with the actual Google People API endpoint path. The gateway proxies requests to `people.googleapis.com` and automatically injects your OAuth 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`:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
export MATON_API_KEY="YOUR_API_KEY"
|
||
|
|
```
|
||
|
|
|
||
|
|
### Getting Your API Key
|
||
|
|
|
||
|
|
1. Sign in or create an account at [maton.ai](https://maton.ai)
|
||
|
|
2. Go to [maton.ai/settings](https://maton.ai/settings)
|
||
|
|
3. Copy your API key
|
||
|
|
|
||
|
|
## Connection Management
|
||
|
|
|
||
|
|
Manage your Google Contacts OAuth connections at `https://ctrl.maton.ai`.
|
||
|
|
|
||
|
|
### List Connections
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python <<'EOF'
|
||
|
|
import urllib.request, os, json
|
||
|
|
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=google-contacts&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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python <<'EOF'
|
||
|
|
import urllib.request, os, json
|
||
|
|
data = json.dumps({'app': 'google-contacts'}).encode()
|
||
|
|
req = urllib.request.Request('https://ctrl.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
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python <<'EOF'
|
||
|
|
import urllib.request, os, json
|
||
|
|
req = urllib.request.Request('https://ctrl.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:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"connection": {
|
||
|
|
"connection_id": "21fd90f9-5935-43cd-b6c8-bde9d915ca80",
|
||
|
|
"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": "google-contacts",
|
||
|
|
"metadata": {}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Open the returned `url` in a browser to complete OAuth authorization.
|
||
|
|
|
||
|
|
### Delete Connection
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python <<'EOF'
|
||
|
|
import urllib.request, os, json
|
||
|
|
req = urllib.request.Request('https://ctrl.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 Google Contacts connections, specify which one to use with the `Maton-Connection` header:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python <<'EOF'
|
||
|
|
import urllib.request, os, json
|
||
|
|
req = urllib.request.Request('https://gateway.maton.ai/google-contacts/v1/people/me/connections?personFields=names&pageSize=10')
|
||
|
|
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
|
||
|
|
req.add_header('Maton-Connection', '21fd90f9-5935-43cd-b6c8-bde9d915ca80')
|
||
|
|
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
If omitted, the gateway uses the default (oldest) active connection.
|
||
|
|
|
||
|
|
## API Reference
|
||
|
|
|
||
|
|
### Contact Operations
|
||
|
|
|
||
|
|
#### List Contacts
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/people/me/connections?personFields=names,emailAddresses,phoneNumbers&pageSize=100
|
||
|
|
```
|
||
|
|
|
||
|
|
**Query Parameters:**
|
||
|
|
- `personFields` (required): Comma-separated list of fields to return (see Person Fields section)
|
||
|
|
- `pageSize`: Number of contacts to return (max 1000, default 100)
|
||
|
|
- `pageToken`: Token for pagination
|
||
|
|
- `sortOrder`: `LAST_MODIFIED_ASCENDING`, `LAST_MODIFIED_DESCENDING`, `FIRST_NAME_ASCENDING`, or `LAST_NAME_ASCENDING`
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"connections": [
|
||
|
|
{
|
||
|
|
"resourceName": "people/c1234567890",
|
||
|
|
"names": [{"displayName": "John Doe", "givenName": "John", "familyName": "Doe"}],
|
||
|
|
"emailAddresses": [{"value": "john@example.com"}],
|
||
|
|
"phoneNumbers": [{"value": "+1-555-0123"}]
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"totalPeople": 1,
|
||
|
|
"totalItems": 1,
|
||
|
|
"nextPageToken": "..."
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Get Contact
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/people/{resourceName}?personFields=names,emailAddresses,phoneNumbers
|
||
|
|
```
|
||
|
|
|
||
|
|
Use the resource name from list or create operations (e.g., `people/c1234567890`).
|
||
|
|
|
||
|
|
#### Create Contact
|
||
|
|
|
||
|
|
```bash
|
||
|
|
POST /google-contacts/v1/people:createContact
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"names": [{"givenName": "John", "familyName": "Doe"}],
|
||
|
|
"emailAddresses": [{"value": "john@example.com"}],
|
||
|
|
"phoneNumbers": [{"value": "+1-555-0123"}],
|
||
|
|
"organizations": [{"name": "Acme Corp", "title": "Engineer"}]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Update Contact
|
||
|
|
|
||
|
|
```bash
|
||
|
|
PATCH /google-contacts/v1/people/{resourceName}:updateContact?updatePersonFields=names,emailAddresses
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"etag": "%EgcBAgkLLjc9...",
|
||
|
|
"names": [{"givenName": "John", "familyName": "Smith"}],
|
||
|
|
"emailAddresses": [{"value": "john.smith@example.com"}]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Note:** Include the `etag` from the get/list response to ensure you're updating the latest version.
|
||
|
|
|
||
|
|
#### Delete Contact
|
||
|
|
|
||
|
|
```bash
|
||
|
|
DELETE /google-contacts/v1/people/{resourceName}:deleteContact
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Batch Get Contacts
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/people:batchGet?resourceNames=people/c123&resourceNames=people/c456&personFields=names,emailAddresses
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Batch Create Contacts
|
||
|
|
|
||
|
|
```bash
|
||
|
|
POST /google-contacts/v1/people:batchCreateContacts
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"contacts": [
|
||
|
|
{
|
||
|
|
"contactPerson": {
|
||
|
|
"names": [{"givenName": "Alice", "familyName": "Smith"}],
|
||
|
|
"emailAddresses": [{"value": "alice@example.com"}]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"contactPerson": {
|
||
|
|
"names": [{"givenName": "Bob", "familyName": "Jones"}],
|
||
|
|
"emailAddresses": [{"value": "bob@example.com"}]
|
||
|
|
}
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"readMask": "names,emailAddresses"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Batch Delete Contacts
|
||
|
|
|
||
|
|
```bash
|
||
|
|
POST /google-contacts/v1/people:batchDeleteContacts
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"resourceNames": ["people/c123", "people/c456"]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Search Contacts
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/people:searchContacts?query=John&readMask=names,emailAddresses
|
||
|
|
```
|
||
|
|
|
||
|
|
**Note:** Search results may have a slight delay for newly created contacts due to indexing.
|
||
|
|
|
||
|
|
### Contact Group Operations
|
||
|
|
|
||
|
|
#### List Contact Groups
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/contactGroups?pageSize=100
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"contactGroups": [
|
||
|
|
{
|
||
|
|
"resourceName": "contactGroups/starred",
|
||
|
|
"groupType": "SYSTEM_CONTACT_GROUP",
|
||
|
|
"name": "starred",
|
||
|
|
"formattedName": "Starred"
|
||
|
|
},
|
||
|
|
{
|
||
|
|
"resourceName": "contactGroups/abc123",
|
||
|
|
"groupType": "USER_CONTACT_GROUP",
|
||
|
|
"name": "Work",
|
||
|
|
"formattedName": "Work",
|
||
|
|
"memberCount": 5
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"totalItems": 2
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Get Contact Group
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/contactGroups/{resourceName}?maxMembers=100
|
||
|
|
```
|
||
|
|
|
||
|
|
Use `contactGroups/starred`, `contactGroups/family`, etc. for system groups, or the resource name for user groups.
|
||
|
|
|
||
|
|
#### Create Contact Group
|
||
|
|
|
||
|
|
```bash
|
||
|
|
POST /google-contacts/v1/contactGroups
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"contactGroup": {
|
||
|
|
"name": "Work Contacts"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Delete Contact Group
|
||
|
|
|
||
|
|
```bash
|
||
|
|
DELETE /google-contacts/v1/contactGroups/{resourceName}?deleteContacts=false
|
||
|
|
```
|
||
|
|
|
||
|
|
Set `deleteContacts=true` to also delete the contacts in the group.
|
||
|
|
|
||
|
|
#### Batch Get Contact Groups
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/contactGroups:batchGet?resourceNames=contactGroups/starred&resourceNames=contactGroups/family
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Modify Group Members
|
||
|
|
|
||
|
|
Add or remove contacts from a group:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
POST /google-contacts/v1/contactGroups/{resourceName}/members:modify
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"resourceNamesToAdd": ["people/c123", "people/c456"],
|
||
|
|
"resourceNamesToRemove": ["people/c789"]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Other Contacts
|
||
|
|
|
||
|
|
Other contacts are people you've interacted with (e.g., via email) but haven't explicitly added to your contacts.
|
||
|
|
|
||
|
|
#### List Other Contacts
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/otherContacts?readMask=names,emailAddresses&pageSize=100
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Copy Other Contact to My Contacts
|
||
|
|
|
||
|
|
```bash
|
||
|
|
POST /google-contacts/v1/{resourceName}:copyOtherContactToMyContactsGroup
|
||
|
|
Content-Type: application/json
|
||
|
|
|
||
|
|
{
|
||
|
|
"copyMask": "names,emailAddresses,phoneNumbers"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Person Fields
|
||
|
|
|
||
|
|
Use these fields with `personFields` or `readMask` parameters:
|
||
|
|
|
||
|
|
| Field | Description |
|
||
|
|
|-------|-------------|
|
||
|
|
| `names` | Display name, given name, family name |
|
||
|
|
| `emailAddresses` | Email addresses with type |
|
||
|
|
| `phoneNumbers` | Phone numbers with type |
|
||
|
|
| `addresses` | Postal addresses |
|
||
|
|
| `organizations` | Company, title, department |
|
||
|
|
| `biographies` | Bio/notes about the person |
|
||
|
|
| `birthdays` | Birthday information |
|
||
|
|
| `urls` | Website URLs |
|
||
|
|
| `photos` | Profile photos |
|
||
|
|
| `memberships` | Contact group memberships |
|
||
|
|
| `metadata` | Source and update information |
|
||
|
|
|
||
|
|
Multiple fields: `personFields=names,emailAddresses,phoneNumbers,organizations`
|
||
|
|
|
||
|
|
## Pagination
|
||
|
|
|
||
|
|
Use `pageSize` and `pageToken` for pagination:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
GET /google-contacts/v1/people/me/connections?personFields=names&pageSize=100&pageToken=NEXT_PAGE_TOKEN
|
||
|
|
```
|
||
|
|
|
||
|
|
Response includes pagination info:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"connections": [...],
|
||
|
|
"totalPeople": 500,
|
||
|
|
"nextPageToken": "...",
|
||
|
|
"nextSyncToken": "..."
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Continue fetching with `pageToken` until `nextPageToken` is not returned.
|
||
|
|
|
||
|
|
## Code Examples
|
||
|
|
|
||
|
|
### JavaScript
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const response = await fetch(
|
||
|
|
'https://gateway.maton.ai/google-contacts/v1/people/me/connections?personFields=names,emailAddresses&pageSize=50',
|
||
|
|
{
|
||
|
|
headers: {
|
||
|
|
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
const data = await response.json();
|
||
|
|
```
|
||
|
|
|
||
|
|
### Python
|
||
|
|
|
||
|
|
```python
|
||
|
|
import os
|
||
|
|
import requests
|
||
|
|
|
||
|
|
response = requests.get(
|
||
|
|
'https://gateway.maton.ai/google-contacts/v1/people/me/connections',
|
||
|
|
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'},
|
||
|
|
params={
|
||
|
|
'personFields': 'names,emailAddresses,phoneNumbers',
|
||
|
|
'pageSize': 50
|
||
|
|
}
|
||
|
|
)
|
||
|
|
data = response.json()
|
||
|
|
```
|
||
|
|
|
||
|
|
## Notes
|
||
|
|
|
||
|
|
- Resource names for contacts follow the pattern `people/c{id}` (e.g., `people/c1234567890`)
|
||
|
|
- Resource names for contact groups follow the pattern `contactGroups/{id}` (e.g., `contactGroups/abc123`)
|
||
|
|
- System contact groups include: `starred`, `friends`, `family`, `coworkers`, `myContacts`, `all`, `blocked`
|
||
|
|
- The `personFields` parameter is required for most read operations
|
||
|
|
- When updating contacts, include the `etag` to prevent overwriting concurrent changes
|
||
|
|
- Mutate requests for the same user should be sent sequentially to avoid increased latency and failures
|
||
|
|
- IMPORTANT: When using curl commands, use `curl -g` when URLs contain brackets to disable glob parsing
|
||
|
|
- IMPORTANT: When piping curl output to `jq` or other commands, environment variables like `$MATON_API_KEY` may not expand correctly in some shell environments
|
||
|
|
|
||
|
|
## Error Handling
|
||
|
|
|
||
|
|
| Status | Meaning |
|
||
|
|
|--------|---------|
|
||
|
|
| 400 | Missing Google Contacts connection or invalid request |
|
||
|
|
| 401 | Invalid or missing Maton API key |
|
||
|
|
| 403 | Permission denied (check OAuth scopes) |
|
||
|
|
| 404 | Contact or group not found |
|
||
|
|
| 429 | Rate limited |
|
||
|
|
| 4xx/5xx | Passthrough error from Google People API |
|
||
|
|
|
||
|
|
### Troubleshooting: API Key Issues
|
||
|
|
|
||
|
|
1. Check that the `MATON_API_KEY` environment variable is set:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
echo $MATON_API_KEY
|
||
|
|
```
|
||
|
|
|
||
|
|
2. Verify the API key is valid by listing connections:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
python <<'EOF'
|
||
|
|
import urllib.request, os, json
|
||
|
|
req = urllib.request.Request('https://ctrl.maton.ai/connections')
|
||
|
|
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
|
||
|
|
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
|
||
|
|
EOF
|
||
|
|
```
|
||
|
|
|
||
|
|
### Troubleshooting: Invalid App Name
|
||
|
|
|
||
|
|
1. Ensure your URL path starts with `google-contacts`. For example:
|
||
|
|
|
||
|
|
- Correct: `https://gateway.maton.ai/google-contacts/v1/people/me/connections`
|
||
|
|
- Incorrect: `https://gateway.maton.ai/v1/people/me/connections`
|
||
|
|
|
||
|
|
## Resources
|
||
|
|
|
||
|
|
- [Google People API Overview](https://developers.google.com/people/api/rest)
|
||
|
|
- [People Resource](https://developers.google.com/people/api/rest/v1/people)
|
||
|
|
- [Contact Groups Resource](https://developers.google.com/people/api/rest/v1/contactGroups)
|
||
|
|
- [Person Fields Reference](https://developers.google.com/people/api/rest/v1/people#Person)
|
||
|
|
- [Maton Community](https://discord.com/invite/dBfFAcefs2)
|
||
|
|
- [Maton Support](mailto:support@maton.ai)
|