Skip to content

Developers

Phase Codes

API docs: Phase Codes

A Phase Code is a cost code under a Job or Meet Point. A worker picks one when they clock in. A Work Order has no Phase Codes.

To do thisUse
Set the whole list of one activityphaseCodes on PATCH /activities/{activityId}
Add one Phase CodePOST /activities/{activityId}/phase-codes
Change or retire one Phase CodePATCH /activities/{activityId}/phase-codes/{phaseCodeId}
Change one Phase Code on many JobsPOST /phase-codes/apply

Scopes: activity:read to read, activity:write to write. Only the owner of an activity can change its Phase Codes. See Ownership.

Back to the index · Conventions · Errors

Every endpoint can also return 401 (no key, or a key that is not valid, revoked or expired) and 503 (service_unavailable or instance_mismatch). The tables below list only the other statuses.

The Phase Code object

FieldTypeDescription
phaseCodeIdintegerThe Phase Code's id
activityIdintegerThe Job or Meet Point that it belongs to
codestringTrimmed only. Case and inner spaces are kept. It never changes.
descriptionstring or nullThe effective description: an administrator's override where one is set, else the owner's
isActivebooleanFalse when the Phase Code is retired
sourceSystemstringThe owner
isLocallyOverriddenbooleanTrue when an administrator overrides a field
overriddenarray of stringsThe fields that an override covers

GET /activities/{activityId}/phase-codes

Lists every Phase Code of one Job or Meet Point, active and inactive, ordered by code. GET /activities shows only the active ones.

Scope: activity:read

Path parameters

NameTypeRequiredDefaultAllowed valuesDescription
activityIdintegerYes1 or moreThe activity's id

Example request

curl "$TIMECARD_BASE_URL/activities/48213/phase-codes" \
  -H "X-API-Key: $TIMECARD_API_KEY"

Example response 200 OK

The list is in records at the top level, with no total and no paging.

{
  "status": 200,
  "message": "Success: phase codes retrieved.",
  "records": [
    {
      "phaseCodeId": 48214,
      "activityId": 48213,
      "code": "01-100",
      "description": "Mobilization",
      "isActive": true,
      "sourceSystem": "acme-erp",
      "isLocallyOverridden": false,
      "overridden": []
    },
    {
      "phaseCodeId": 48230,
      "activityId": 48213,
      "code": "03-300",
      "description": "Concrete",
      "isActive": false,
      "sourceSystem": "acme-erp",
      "isLocallyOverridden": false,
      "overridden": []
    }
  ]
}

Errors

StatuscodeWhen
400validation_failedactivityId is not a positive integer
403insufficient_scopeThe key does not have activity:read
404not_foundNo activity has this id within the key's companies
500internal_errorThe Phase Codes could not be read

POST /activities/{activityId}/phase-codes

Adds one Phase Code to a Job or Meet Point. You do not send the whole list again. The new Phase Code is owned by your key's source system and copies the activity's division.

Scope: activity:write

Path parameters

NameTypeRequiredDefaultAllowed valuesDescription
activityIdintegerYes1 or moreThe Job or Meet Point

Request body

NameTypeRequiredDefaultAllowed valuesDescription
codestringYes1 to 50 charactersThe Phase Code. Trimmed only. Case and inner spaces are kept.
descriptionstring or nullNoMax 200 charactersThe description. Blank is stored as null.

Example request

curl -X POST "$TIMECARD_BASE_URL/activities/48213/phase-codes" \
  -H "X-API-Key: $TIMECARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "code": "03-300", "description": "Concrete" }'

Example response 201 Created

{
  "status": 201,
  "message": "Success: phase code added.",
  "record": {
    "phaseCodeId": 48230,
    "activityId": 48213,
    "code": "03-300",
    "description": "Concrete",
    "isActive": true,
    "sourceSystem": "acme-erp",
    "isLocallyOverridden": false,
    "overridden": []
  }
}

Errors

StatuscodeWhen
400validation_failedactivityId is not a positive integer, the body is not valid, or the activity is a Work Order
403insufficient_scopeThe key does not have activity:write
403forbiddenThe key has no source system
404not_foundNo activity has this id within the key's companies
409conflictThe activity belongs to another source system. Or the activity already has this code, active or not. To bring back an inactive code, use PATCH with "isActive": true.
500internal_errorThe Phase Code could not be added

PATCH /activities/{activityId}/phase-codes/{phaseCodeId}

Changes one Phase Code and leaves the others alone. Send description, isActive, or both. The code cannot change.

Scope: activity:write

Path parameters

NameTypeRequiredDefaultAllowed valuesDescription
activityIdintegerYes1 or moreThe Job or Meet Point
phaseCodeIdintegerYes1 or moreOne of the activity's Phase Codes

Request body

Send at least one field. An empty body gets 400.

NameTypeRequiredDefaultAllowed valuesDescription
descriptionstring or nullNoMax 200 charactersThe new description. Blank or null clears it.
isActivebooleanNofalse retires the Phase Code. true brings it back.

The body can also hold revert, but only an administrator can use it. A key that sends revert gets 400.

Example request

curl -X PATCH "$TIMECARD_BASE_URL/activities/48213/phase-codes/48230" \
  -H "X-API-Key: $TIMECARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "description": "Concrete flatwork", "isActive": true }'

Example response 200 OK

{
  "status": 200,
  "message": "Success: phase code updated.",
  "record": {
    "phaseCodeId": 48230,
    "activityId": 48213,
    "code": "03-300",
    "description": "Concrete flatwork",
    "isActive": true,
    "sourceSystem": "acme-erp",
    "isLocallyOverridden": false,
    "overridden": []
  }
}

When the values you send are already stored, nothing changes and the message is Success: nothing changed.

Errors

StatuscodeWhen
400validation_failedAn id is not a positive integer, the body is empty or not valid, or the body sends code
403insufficient_scopeThe key does not have activity:write
403forbiddenThe key has no source system
404not_foundNo activity has this id within the key's companies, or phaseCodeId is not one of its Phase Codes
409conflictThe Phase Code belongs to another source system. Or it is the last active Phase Code of a Meet Point, which must keep one.
500internal_errorThe Phase Code could not be updated

GET /phase-codes

Lists each distinct Phase Code across one company's Jobs, with the number of Jobs that use it. Use it to keep one cost-code list in step across every Job. Codes are grouped without case. Ordered by code.

Scope: activity:read

Query parameters

NameTypeRequiredDefaultAllowed valuesDescription
companyCodestringWhen the key covers more than one companyMax 10 charactersThe company
qstringNoMax 100 charactersKeeps a code when the code, or a Job's description of it, contains this text
isActivestringNoBothtrue, falsetrue keeps codes that are active on at least one active Job. false keeps codes that are active on none.
limitintegerNo251 to 100Page size
offsetintegerNo00 or moreCodes to skip

Example request

curl "$TIMECARD_BASE_URL/phase-codes?companyCode=ACME&q=01-&limit=100" \
  -H "X-API-Key: $TIMECARD_API_KEY"

Example response 200 OK

{
  "status": 200,
  "message": "Success: phase codes retrieved.",
  "record": {
    "total": 1,
    "records": [
      {
        "code": "01-100",
        "description": "Mobilization",
        "descriptions": ["Mobilization", "Mobilisation"],
        "jobCount": 14,
        "activeJobCount": 9
      }
    ]
  }
}

Response record fields

FieldTypeDescription
codestringThe Phase Code
descriptionstring or nullThe description that most Jobs use. Null when no Job gives one.
descriptionsarray of stringsEvery distinct description, most used first
jobCountintegerJobs that have the code, active or not
activeJobCountintegerJobs where the Job and the code are both active

Errors

StatuscodeWhen
400validation_failedA query parameter is not valid, or companyCode is missing when the key covers several companies
403insufficient_scopeThe key does not have activity:read
403forbiddenA key bound to one company names another
404not_foundcompanyCode names no active company that the key can reach
500internal_errorThe Phase Codes could not be read

POST /phase-codes/apply

Adds, renames, deactivates or reactivates one Phase Code on many Jobs at once. Name the Jobs in activityIds, or send "allActiveJobs": true. Send exactly one of the two.

Scope: activity:write

A JobResult
Does not have the codeThe code is added, active. If you send "isActive": false, the Job is left unchanged.
Has the codeIt takes the description and isActive that you send, and keeps what you do not send
Belongs to another source systemRefused
Is not a Job of the companyRefused

Each Job is saved on its own and gets its own result. One refused Job does not stop the others. Send "dryRun": true to see the results first. A dry run saves nothing.

Request body

NameTypeRequiredDefaultAllowed valuesDescription
companyCodestringWhen the key covers more than one companyMax 10 charactersThe company
codestringYes1 to 50 charactersThe Phase Code. Trimmed only.
descriptionstring or nullNoMax 200 charactersWritten where the code is added or present. Leave it out to keep each Job's description.
isActivebooleanNotrue reactivates, false deactivates. Leave it out to add a missing code as active and keep a present one's state.
activityIdsarray of integersOne of activityIds or allActiveJobs1 to 1,000 itemsThe Jobs. Repeats are dropped.
allActiveJobsbooleanOne of activityIds or allActiveJobstrueApply to every active Job of the company
dryRunbooleanNofalsetrue reports what would happen and saves nothing

Example request

curl -X POST "$TIMECARD_BASE_URL/phase-codes/apply" \
  -H "X-API-Key: $TIMECARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "companyCode": "ACME",
    "code": "99-900",
    "description": "Safety meeting",
    "isActive": true,
    "allActiveJobs": true,
    "dryRun": true
  }'

Example response 200 OK

{
  "status": 200,
  "message": "Success: dry run, 1 of 2 jobs would change. Nothing was saved.",
  "record": {
    "dryRun": true,
    "results": [
      { "activityId": 48213, "jobCode": "J-2026-104", "outcome": "added" },
      {
        "activityId": 48400,
        "jobCode": "J-2026-131",
        "outcome": "refused",
        "message": "J-2026-131 is owned by timecard, and its Phase Codes belong to it."
      }
    ]
  }
}

Without dryRun, the message reads Success: <n> of <total> jobs changed. Do not parse the message text of a result. It can change.

Response fields

FieldTypeDescription
record.dryRunbooleanTrue when nothing was saved
record.results[].activityIdintegerThe Job
record.results[].jobCodestring or nullThe Job's code. Null when activityId names no Job of the company.
record.results[].outcomestringadded, updated, reactivated, deactivated, unchanged or refused
record.results[].messagestringWhy the Job was refused, or why nothing changed. Present only then.

Results are in the order sent for activityIds, or by Job code for allActiveJobs.

Errors

StatuscodeWhen
400validation_failedThe body is not valid, sends both or neither of activityIds and allActiveJobs, or companyCode is missing when the key covers several companies
403insufficient_scopeThe key does not have activity:write
403forbiddenA key bound to one company names another
404not_foundcompanyCode names no active company that the key can reach
500internal_errorThe Phase Code could not be applied