Zoho Projects#
Zoho Projects supports native CSV import and OAuth-backed portal sync. Both methods create the same reviewable import session and use one Zoho-owned task projector; the sync also imports active portal/client users and task actors into the user-mapping flow.
| Capability | Method | Status |
|---|---|---|
CSV_IMPORT |
Upload a Zoho Projects task CSV export | Built |
SYNC |
Pull projects/tasks from one selected portal | Built |
USER_IMPORT |
Import active portal/client users during sync | Built during /imports/sync |
WEBHOOK |
Verify Zoho Projects events | Not built |
The routes live under /api/v1/providers/zoho-projects/... and use the shared
provider-parameterized handlers. Organization data requires an organization-scoped user
token; see Authentication.
OAuth Application Setup#
Create a Server-based Application in the Zoho API Console. Configure the exact callback for the environment running this service:
# Local API
http://127.0.0.1:8057/api/v1/providers/zoho-projects/oauth/callback
# TeamBoost integrations testing API
https://tb-test-integrations.atlas-labs.org/api/v1/providers/zoho-projects/oauth/callback
The URI in Zoho and ZOHO_PROJECTS_REDIRECT_URI must match character for character. If
users in multiple data centers will connect, enable those data centers in the application's
Multi-DC settings and choose Use the same OAuth credentials for all data centers; this
adapter currently configures one service-wide client secret.
Configure these values in the service environment/secret manager, never in source control:
ZOHO_PROJECTS_CLIENT_ID=<server-app-client-id>
ZOHO_PROJECTS_CLIENT_SECRET=<server-app-client-secret>
ZOHO_PROJECTS_REDIRECT_URI=https://<api-host>/api/v1/providers/zoho-projects/oauth/callback
ZOHO_PROJECTS_SCOPES=ZohoProjects.portals.READ,ZohoProjects.projects.READ,ZohoProjects.tasklists.READ,ZohoProjects.tasks.READ,ZohoProjects.users.READ
The callback requires Zoho's paired location and accounts-server values. Supported
locations are US, EU, IN, AU, JP, CA, SA, and UK. The pair is checked against exact regional
Accounts origins; the token's API domain is separately allowlisted. Projects requests use
the exact regional projectsapi origin selected for that validated location. There is no
silent US fallback and no provider-supplied URL is followed.
Authorizing a Portal#
Start authorization through the generic route:
GET /api/v1/providers/zoho-projects/oauth/authorize?organization_id=<id>
The response contains a short-lived signed state and authorizationUrl. Open the URL in
a browser and approve access. Zoho redirects directly to this service with code, state,
location, and accounts-server; the callback exchanges the one-time code and stores the
token/portal/routing bundle encrypted for that organization. Access and refresh tokens are
never returned.
If the Zoho user can access exactly one portal, that portal is selected. If multiple portals are visible, start again with the intended portal id signed into state:
GET /api/v1/providers/zoho-projects/oauth/authorize?organization_id=<id>&targetAccountId=<portal-id>
The selected id must match an accessible portal. The adapter never chooses the first of multiple portals. The portal id can be read from the portal/API URL or portal settings in Zoho Projects; it is not a client secret.
Swagger test sequence#
At https://tb-test-integrations.atlas-labs.org/docs:
- Use Authorize to set the TeamBoost user Bearer token.
- Run
GET /api/v1/providers/zoho-projects/oauth/authorizewith the TeamBoostorganization_id(andtargetAccountIdonly when multiple portals are accessible). - Copy
authorizationUrlfrom the response into the browser and complete Zoho consent. Do not try to reuse the returned authorization code. - Run
POST /api/v1/providers/zoho-projects/credentials/verify?organization_id=<id>. - Run
POST /api/v1/providers/zoho-projects/imports/sync?organization_id=<id>. - Inspect the returned import session, user mappings, projected drafts, projection errors, and mapping notes. This creates a review session; it does not execute TeamBoost tasks.
If step 3 reports multiple portals, obtain the intended portal id and repeat from step 2
with targetAccountId. A failed/consumed authorization code cannot be replayed.
API Sync Behavior#
POST /api/v1/providers/zoho-projects/credentials/verify?organization_id=<id>
POST /api/v1/providers/zoho-projects/imports/sync?organization_id=<id>
Credential verification confirms that the current token can still see the stored portal. Before verify/sync, an expiring access token is refreshed through the same regional Accounts origin; Zoho refresh responses may omit the existing refresh token, API domain, and scopes, so the validated encrypted values are retained.
One sync:
- pages through all projects in the selected portal;
- pages through every project's task lists and tasks via V3, hydrating task-list ids into task-group names;
- pages through active portal users and active client users via V3.1;
- joins directory
users[].idto task actorzpuid, while retaining task-only actors; - uses up to 200 records per page and refuses malformed/repeated pagination;
- retries network errors,
429, and transient5xxresponses at most four times, honoring a boundedRetry-After; auth and other4xxfailures are terminal.
Zoho documents per-endpoint rate limits. A sync that exhausts the bounded retry policy fails loudly; it does not create a partial import session.
API User Mapping#
V3.1 directory ids and task actor zpuid values are the durable mapping key. E-mail and
display name remain matching hints. The first task owner in source order is selected as the
normalized owner; every owner remains in the lossless source record and multiple owners add
a review note.
- a present but unmapped owner creates a blocking projection error;
- a mapped owner becomes
ownerId; - a mapped creator becomes
creatorId; - an unmapped creator leaves
creatorIdunset with a non-blocking note.
Exporting a Compatible CSV#
In Zoho Projects, open the task view to export, choose CSV as the file type, and leave
password protection disabled. The parser accepts native plain-text CSV, including the
optional Export with file information preamble. It does not accept encrypted Office
workbooks or Excel files renamed with a .csv extension.
Include these selected columns:
Task NameOwnerCustom StatusPriorityCreated ByProject NameTask IDTask System IDTask List NameLast Modified TimeCreated Time
Additional selected columns are retained losslessly. The parser finds the task header after the optional file-information preamble.
Importing From CSV#
POST /api/v1/providers/zoho-projects/imports/csv?organization_id=<id>
curl -X POST "https://<host>/api/v1/providers/zoho-projects/imports/csv?organization_id=590789" \
-H "Authorization: Bearer <user-token>" \
-F "file=@zoho-projects-task-export.csv"
Task System ID is the stable provider identity; Task ID is the human identifier. A row
also requires Task Name, Created Time, and Last Modified Time. Malformed rows are
returned in rowErrors; a missing required header fails with invalid_csv_format.
CSV dates are MM-DD-YYYY without a time zone/time-of-day. They become midnight UTC with
date_only precision recorded in source context. A literal - is absent. CSV actors are
name-only and matched through sourceName; the parser treats each actor cell as one opaque
display name rather than guessing a multi-owner delimiter.
Projection Field Coverage#
| Zoho Projects source | TeamBoost draft field | Behavior |
|---|---|---|
| task name | title |
Error if shorter than 2 characters |
| task | type |
Defaults to OTHER with a mapping note |
| description | description |
Optional |
| status/custom status | status |
Open/to-do → NOT_STARTED; active/in-progress → IN_PROGRESS; completed/closed/done → COMPLETED; canceled → CANCELED; Blocked/On Hold → BLOCKED; unknown values are unset |
| priority | priority |
urgent/critical/high/medium/low map directly; none/blank/unknown → UNKNOWN |
first owner / zpuid |
ownerId |
Resolved through the proposed user mapping |
creator / zpuid |
creatorId |
Resolved when possible; no fallback id is invented |
| project name | projectTitle |
Optional |
| task-list name | taskGroupTitle |
Optional |
| API tags | labels |
Each returned tag name is retained as a label; CSV tag cells stay lossless but unmapped because their delimiter is not provider-proven |
| start date | plannedStartDate |
CSV date-only or API ISO-8601 |
| due date | plannedEndDate |
CSV date-only or API ISO-8601 |
| selected CSV fields or complete V3 task/context | descriptionJson |
Preserved for review/trace |
Duration/work values, phases, and relationships remain in descriptionJson; they are not
converted into TeamBoost duration or dependencies. API tags also map to labels, while CSV
tags remain only in descriptionJson.
Not Built Yet#
- Standalone user import outside
/imports/sync. - Webhook registration, signature verification, timestamp/replay protection, and event processing.