Tracopus · Integration API

Connect an external service to create projects

Issue a scoped integration token, then POST /api/v2/projects with that token. Sandbox vs production only changes the key prefix — scopes are the same live catalog.

v2 API Bearer tk_live_ / tk_test_ projects.write

How it works

  1. An admin issues an integration token in Tracopus (UI or admin JWT curl). The secret is shown once.
  2. The external service stores tk_live_… or tk_test_… as a secret. Do not put it in source control.
  3. The service calls POST /api/v2/projects with Authorization: Bearer {secret}.
  4. Tracopus binds the write to the key’s team. userTeamId in the body must match that team (or omit it and the server fills it).
  5. createdBy is overwritten with the key’s service employee for audit.
Integration keys cannot call /api/v2/developer-portal/*. Key issue/revoke stays on an admin session.

Canonical chain

Projects are not independent. Create against an existing purchase order in the same team:

PurchaseOrder → Project → Task → TaskActivity

Minimum body for a working create:

{
  "name": "ERP project",
  "userTeamId": 101,
  "purchaseOrderId": 9001,
  "createdBy": 12
}

Prerequisites

ItemWhy
Server flag FS_INTEGRATION_API_KEY_AUTH_ENABLED (default true) Maps to fs.integration.api-key-auth.enabled. If false, tk_* tokens do not authenticate (401).
Admin with Integrations → API access UI path /integrations/api-access (or Admin → API keys). Developer portal + integration center must be enabled for the org.
Team id, purchase order id, service employee id The PO must already exist on that team. The employee on the key is used as createdBy.
Scope projects.write projects.read cannot create. Extra sandbox scopes do not exist — only the catalog below.

1 · Issue the API key

Preferred: Tracopus → Integrations → API access → Create token. Choose Production (tk_live_) or Sandbox (tk_test_), grant projects.write, copy the secret immediately.

Or create with an admin JWT (not an integration key):

POST /api/v2/developer-portal/api-keys
curl -sS -X POST "$BASE/api/v2/developer-portal/api-keys?userTeamId=TEAM_ID" \
  -H "Authorization: Bearer ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -H "Uid: EMPLOYEE_ID" \
  -H "Current-User-Team-Id: TEAM_ID" \
  -d '{
    "name": "ERP project sync",
    "appName": "External ERP",
    "environment": "production",
    "scopes": ["projects.write"],
    "serviceEmployeeId": EMPLOYEE_ID,
    "createdByEmployeeId": EMPLOYEE_ID
  }'
FieldRequiredNotes
userTeamId queryYesTeam that owns the key. Project creates must use this same team.
nameYesAudit label, e.g. ERP project sync.
appNameNoDefaults to Custom integration.
environmentNoproduction → tk_live_. sandbox → tk_test_. Same scopes either way.
scopesYesMust include projects.write to create projects. Non-catalog values are dropped.
serviceEmployeeIdYes for writesEmployee stamped onto createdBy when the key creates a project.

Success envelope (secret only on create):

{
  "payload": {
    "key": { "id": "key-…", "prefix": "tk_live_8f2a", "scopes": ["projects.write"], "status": "active" },
    "secretOnce": "tk_live_8f2a_<uuid>_<tail>"
  }
}
Store secretOnce in your secret manager. Tracopus will not show it again. Use the full string as the Bearer token — not the prefix alone.

2 · Create a project

Base URL is your Tracopus API host (example: https://app.tracopus.com). No SPA api-key query param is required for integration tokens.

POST /api/v2/projects
curl -sS -X POST "$BASE/api/v2/projects" \
  -H "Authorization: Bearer tk_live_SUFFIX_UUID_TAIL" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ERP project",
    "userTeamId": 101,
    "purchaseOrderId": 9001,
    "createdBy": 12,
    "summary": "Created from ERP sync",
    "status": "NEW",
    "type": "BILLABLE"
  }'

Typical 200 envelope:

{
  "payload": {
    "id": 44102,
    "name": "ERP project",
    "userTeamId": 101,
    "purchaseOrderId": 9001,
    "status": "NEW",
    "createdBy": 12
  }
}

Project request body

JSON object mapped from ProjectRequest / ProjectInfo. Dates are ISO-8601 date-times.

FieldTypeNotes
namerequiredstringProject display name.
purchaseOrderIdrequirednumberExisting PO on the same team. Projects hang off this contract.
userTeamIdrequired*numberMust equal the key’s team. Omit to let the server inject it. Mismatch → 403.
createdBysentnumberOverwritten with the key’s serviceEmployeeId.
summaryoptionalstringShort description.
statusoptionalstringNEW, CREATED, ASSIGNED, INPROGRESS, COMPLETED, CLOSED.
type / projectTypeoptionalstringDelivery classification used by your org (e.g. billable vs internal).
codeoptionalstringExternal/project code if you maintain one.
owneroptionalnumberEmployee id of the delivery owner.
estStartTimeoptionaldatetimee.g. 2026-09-01T00:00:00.
estCompletionTimeoptionaldatetimePlanned end.
estHoursoptionalnumberEstimated hours.
currencyoptionalstringISO currency if set on the project.
locationoptionalstringProject location.
clientNamesoptionalstring[]Client labels.
watchersoptionalstring[]Watcher identifiers as stored by Tracopus.
accountNameoptionalstringAccount label.
priority / complexityoptionalstringOrg-defined values.
Do not send a different userTeamId than the key’s team. Query ?userTeamId= on other endpoints is also locked to the key team.

Scope catalog

Production and sandbox keys share this catalog. There is no sandbox.read / sandbox.write.

ScopeAllows
projects.readGET /api/v2/projects*
projects.writePOST / PUT / PATCH / DELETE /api/v2/projects* — required to create
purchaseorders.readGET /api/v2/purchaseorder*
purchaseorders.writeWrite purchase orders
tasks.read / tasks.write/api/v2/task*
employees.readGET /api/v2/employees*
integrations.readGET /api/v2/integrations/center*

Auth, headers, errors

HeaderCreate key (admin)Create project (integration)
AuthorizationBearer admin JWTBearer tk_live_… or tk_test_…
Content-Typeapplication/jsonapplication/json
UidAdmin employee idNot required
Current-User-Team-IdAdmin teamNot required
StatusMeaning
200Created. Project is under payload.
401 / empty authMissing Bearer, invalid secret, or API-key auth disabled on the server.
403Missing projects.write, team mismatch, or developer-portal path called with an integration key. Body: {"error":[{"message":"Integration API key scope or team access denied"}]} or userTeamId must match integration key team.

Curl builder

Fill your values, generate, copy. Nothing is sent from this page.

Generated curl