通过 OAuth 代理调用 Zoho Mail REST API,管理账户、文件夹、标签以及邮件收发。
文档
zoho-projects
试用通过托管 OAuth 网关读写 Zoho Projects 资源,所有写操作执行前需用户确认。
它能做什么
基于 Maton 网关调用 Zoho Projects API V3,统一通过 https://api.maton.ai/zoho-projects/api/v3/ 访问。读接口覆盖门户、项目、任务、任务列表、里程碑、任务评论与用户等资源;写操作(POST、PATCH、DELETE)在执行前必须先与用户核对目标与影响。鉴权依赖 Bearer MATON_API_KEY,并要求事先由用户走完 Maton 的 OAuth 连接流程。
什么时候用它
- 罗列已授权 Zoho 账户下的门户与项目
- 创建、更新或关闭任务与任务列表
- 为任务添加或删除评论
- 管理项目里程碑并查询门户成员
技能文档
Zoho Projects
Access the Zoho Projects API V3 with managed OAuth authentication. Manage projects, tasks, milestones, tasklists, and team collaboration.
Quick Start
# List all portals
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.maton.ai/zoho-projects/api/v3/portals')
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/zoho-projects/api/v3/{endpoint}
The gateway proxies requests to projectsapi.zoho.com and automatically injects your OAuth token.
Important:
- V3 endpoints use
/api/v3/prefix (not/restapi/) - No trailing slashes on endpoint paths
- Request bodies are JSON (
Content-Type: application/json) - Updates use PATCH method (not POST)
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
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Zoho Projects OAuth 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=zoho-projects&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': 'zoho-projects'}).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": "2026-02-28T00:12:25.223434Z",
"last_updated_time": "2026-02-28T00:16:32.882675Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "zoho-projects",
"metadata": {},
"method": "OAUTH2"
}
}
Open the returned url in a browser to complete OAuth 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 Zoho Projects 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/zoho-projects/api/v3/portals')
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
- No implicit access: The MATON_API_KEY alone cannot access Zoho Projects. The user must explicitly authorize via OAuth through Maton's connect flow.
- Scoped access: Access is limited to the specific Zoho Projects account the user authorized.
- Write safeguards: All write operations (POST, PATCH, DELETE) require explicit user approval. Before executing any create, update, or delete call, confirm the target resource and intended effect with the user.
API Reference
Portals
List Portals
GET /zoho-projects/api/v3/portals
Response:
[
{
"id": "916020774",
"portal_name": "mycompany",
"org_name": "mycompany",
"timezone": "PST",
"project_plan": "Free",
"owner": {
"zpuid": "2644874000000085003",
"name": "John Doe",
"email": "john@example.com"
},
"profile": {
"name": "Portal Owner",
"id": 2644874000000085084
}
}
]
Projects
List Projects
GET /zoho-projects/api/v3/portal/{portal_id}/projects
Query parameters: page, per_page, status (active, archived, template)
Response:
[
{
"id": "2644874000000089119",
"key": "NU-1",
"name": "My Project",
"project_type": "active",
"description": "Project description",
"owner": {
"zpuid": "2644874000000085003",
"name": "John Doe",
"email": "john@example.com"
},
"is_public_project": false,
"created_time": "2026-02-27T10:20:22.421Z",
"modified_time": "2026-02-27T10:20:22.421Z"
}
]
Get Project Details
GET /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}
Create Project
POST /zoho-projects/api/v3/portal/{portal_id}/projects
Content-Type: application/json
{
"name": "New Project",
"description": "Project description"
}
Response (201):
{
"id": "2644874000000096003",
"key": "NU-2",
"name": "New Project",
"project_type": "active",
"description": "Project description",
"owner": {
"zpuid": "2644874000000085003",
"name": "John Doe"
},
"created_time": "2026-05-17T22:08:52.537Z"
}
Update Project
PATCH /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}
Content-Type: application/json
{
"name": "Updated Name",
"description": "Updated description"
}
Delete Project
DELETE /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}
Returns 204 No Content on success.
Tasks
List Tasks
GET /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks
Query parameters: page, per_page, owner, status, priority, tasklist_id, sort_by
Response:
{
"page_info": {
"page": 1,
"per_page": 100,
"page_count": 3,
"has_next_page": false
},
"tasks": [
{
"id": "2644874000000089247",
"prefix": "EZ1-T1",
"name": "Task 1",
"status": {
"id": "2644874000000016068",
"name": "Open",
"is_closed_type": false
},
"priority": "none",
"project": {
"id": "2644874000000089119",
"name": "My Project"
},
"tasklist": {
"id": "2644874000000089245",
"name": "General"
},
"milestone": {
"id": "2644874000000000073",
"name": "None"
}
}
]
}
Get Task Details
GET /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks/{task_id}
Create Task
POST /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks
Content-Type: application/json
{
"name": "New Task",
"priority": "high",
"description": "Task description",
"tasklist_id": "{tasklist_id}"
}
Optional fields: person_responsible, tasklist_id, start_date, end_date, priority, description
Response (201): Returns the created task object.
Update Task
PATCH /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks/{task_id}
Content-Type: application/json
{
"name": "Updated Task Name",
"priority": "medium"
}
Delete Task
DELETE /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks/{task_id}
Returns 204 No Content on success.
Task Comments
List Comments
GET /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks/{task_id}/comments
Response:
{
"page_info": {
"per_page": 100,
"has_next_page": false,
"count": 1,
"page": 1
},
"comments": [
{
"id": "2644874000000094015",
"comment": "This is a comment",
"created_time": "2026-05-17T22:08:51.264Z",
"created_by": {
"zpuid": "2644874000000085003",
"name": "John Doe"
}
}
]
}
Add Comment
POST /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks/{task_id}/comments
Content-Type: application/json
{
"comment": "This is a comment"
}
Note: The field name is comment, not content.
Response (201): Returns the created comment object.
Delete Comment
DELETE /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks/{task_id}/comments/{comment_id}
Returns 204 No Content on success.
Tasklists
List Tasklists
GET /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasklists
Response:
{
"page_info": {
"page": 1,
"per_page": 200,
"page_count": 1,
"has_next_page": false
},
"tasklists": [
{
"id": "2644874000000089245",
"name": "General",
"flag": "internal",
"status": "active",
"milestone": {
"id": "2644874000000000073",
"name": "None"
},
"created_time": "2026-02-27T10:20:24.426Z"
}
]
}
Create Tasklist
POST /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasklists
Content-Type: application/json
{
"name": "New Tasklist",
"flag": "internal"
}
Optional fields: milestone_id, flag (internal or external)
Response (201): Returns the created tasklist object.
Update Tasklist
PATCH /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasklists/{tasklist_id}
Content-Type: application/json
{
"name": "Updated Tasklist Name"
}
Delete Tasklist
DELETE /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasklists/{tasklist_id}
Returns 204 No Content on success.
Milestones
List Milestones
GET /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/milestones
Response:
{
"page_info": [
{
"per_page": 100,
"has_next_page": false,
"page": 1
}
],
"milestones": [
{
"id": "2644874000000096133",
"name": "Phase 1",
"start_date": "2026-05-17",
"end_date": "2026-06-01",
"flag": "internal",
"owner": {
"zpuid": "2644874000000085003",
"name": "John Doe"
},
"created_time": "2026-05-17T22:09:13.771Z"
}
]
}
Create Milestone
POST /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/milestones
Content-Type: application/json
{
"name": "Phase 1",
"start_date": "06-01-2026",
"end_date": "06-15-2026",
"flag": "internal",
"owner_zpuid": "{user_zpuid}"
}
Required fields: name, start_date, end_date, flag, owner_zpuid
Note: Date format for creating milestones is MM-dd-yyyy.
Response (201): Returns the created milestone object.
Update Milestone
PATCH /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/milestones/{milestone_id}
Content-Type: application/json
{
"name": "Updated Phase",
"end_date": "06-20-2026"
}
Delete Milestone
DELETE /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/milestones/{milestone_id}
Returns 204 No Content on success.
Users
List Users
GET /zoho-projects/api/v3/portal/{portal_id}/users
Response:
{
"page_info": {
"per_page": 100,
"has_next_page": false,
"count": 1,
"page": 1
},
"users": [
{
"zpuid": "2644874000000085003",
"name": "John Doe",
"email": "john@example.com",
"is_active": true,
"role": {
"name": "Administrator",
"id": "2644874000000085005"
},
"added_time": "2026-02-27T10:19:11.719Z"
}
]
}
Pagination
V3 uses page-based pagination with page and per_page parameters:
GET /zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks?page=1&per_page=50
Response includes page_info:
{
"page_info": {
"page": 1,
"per_page": 50,
"page_count": 25,
"has_next_page": true
},
"tasks": [...]
}
When has_next_page is true, increment page to get the next batch.
Code Examples
JavaScript
// List tasks in a project
const response = await fetch(
'https://api.maton.ai/zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
console.log(data.tasks);
Python
import os
import requests
# Create a task
response = requests.post(
'https://api.maton.ai/zoho-projects/api/v3/portal/{portal_id}/projects/{project_id}/tasks',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
},
json={'name': 'New Task', 'priority': 'high'}
)
task = response.json()
print(task['id'])
Notes
- V3 API uses
/api/v3/prefix — do NOT use trailing slashes - All POST/PATCH requests use
application/jsoncontent type (not form-urlencoded like V2) - Updates use PATCH method (not POST like V2)
- Portal ID is required for most endpoints — obtain from
GET /api/v3/portals - Date format for milestone creation:
MM-dd-yyyy(e.g.,06-01-2026) - Pagination uses
page+per_page(notindex+rangelike V2) - Delete operations return 204 No Content
- Create operations return 201 Created
Error Handling
| Status | Meaning |
|---|---|
| 201 | Resource created successfully |
| 204 | Success with no content (delete operations) |
| 400 | Missing/invalid input parameter or invalid URL |
| 401 | Invalid or missing API key, or invalid OAuth scope |
| 404 | Resource not found |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Zoho Projects API |
V3 error format:
{
"error": {
"status_code": "400",
"title": "LESS_THAN_MIN_OCCURANCE",
"error_type": "FIELDS_VALIDATION_ERROR",
"details": [
{
"message": "Input Parameter Missing",
"field_name": "comment"
}
]
}
}
Troubleshooting: API Key Issues
- Check that the
MATON_API_KEYenvironment variable is set:
echo $MATON_API_KEY
- Verify the API key is valid by listing connections:
python <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://api.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
Ensure your URL path starts with zoho-projects. For example:
- Correct:
https://api.maton.ai/zoho-projects/api/v3/portals - Incorrect:
https://api.maton.ai/api/v3/portals
Troubleshooting: Trailing Slashes
V3 does NOT allow trailing slashes. For example:
- Correct:
https://api.maton.ai/zoho-projects/api/v3/portal/{portal_id}/projects - Incorrect:
https://api.maton.ai/zoho-projects/api/v3/portal/{portal_id}/projects/
Resources
相关技能
通过托管 OAuth 网关读写 Zoho 日历的日历与事件。
通过 Maton 托管的 OAuth 代理读写 Zoho CRM 记录,所有写入操作均需用户确认。
通过托管 OAuth 连接 Zoho Inventory API,对商品、销售订单、发票、联系人、账单等记录进行读写。
通过 Maton API 代理,使用托管 OAuth 管理 Zoho Bookings 的预约、服务、员工和工作区。
通过托管 OAuth 读写 Zoho Recruit 的候选人、职位和面试数据。