Clients
Agency client book request and response fields.
Base URL https://api.klockwork.org. Send X-Klockwork-Key or Authorization: Bearer kw_…. An agency key acts as the agency that created it. JSON bodies use these fields. A missing required field returns 400 with { "error": "Validation failed", "details": {} }.
List clients
GET /api/public/agency/clients
Query:
| Field | Type | Required |
|---|---|---|
status | "active" or "archived" or "all" | no |
Response 200: { clients: Client[] }
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
clerkOrgId | string, min 1 | yes |
displayName | string, min 1 | yes |
contactPersonName | string, min 1 | yes |
invoiceEmail | string (email) | yes |
hourlyRate | number, min 0 | yes |
currency | string, min 1, max 8 | yes |
hoursPerDay | number | yes |
shiftStart | string | yes |
shiftEnd | string | yes |
timezone | string, min 1 | yes |
status | "active" or "archived" | yes |
unpaidBreakHours | number, min 0 or null | yes |
billingAddress | string or null | yes |
billingStreet | string or null | yes |
billingCity | string or null | yes |
billingState | string or null | yes |
billingPostal | string or null | yes |
billingCountry | string or null | yes |
paymentTermsOverride | string or null | yes |
internalNotes | string or null | yes |
colorTag | string or null | yes |
clientClerkOrgId | string, min 1 or null | yes |
clerkInvitationId | string, min 1 or null | yes |
invitationStatus | "pending" or "accepted" or "expired" or "revoked" or null | yes |
inviteToken | string (uuid) or null | yes |
seatKind | "full" or "partial" or null | yes |
seatStatus | "pending" or "active" or "ended" or "grandfathered" | yes |
seatAmountCents | integer, min 0 or null | yes |
coversPremium | boolean | yes |
acceptedAt | string or null | yes |
endedAt | string or null | yes |
createdAt | string | yes |
updatedAt | string | yes |
Create a client
POST /api/public/agency/clients
The table below is client. Beside it: invitationSent boolean, linkedImmediately boolean, inviteUrl string or null, and seatQuote with kind (full, partial, or unknown), amountCents integer or null, and label string. A duplicate invoice email returns 400 { "error": "A client with this invoice email is already in the book" }.
Request body:
| Field | Type | Required |
|---|---|---|
displayName | string, min 1 | yes |
contactPersonName | string, min 1 | yes |
invoiceEmail | string (email) | yes |
hourlyRate | number, min 0 | yes |
currency | string, min 1, max 8 | yes |
hoursPerDay | number | yes |
shiftStart | string | yes |
shiftEnd | string | yes |
timezone | string, min 1 | yes |
status | "active" or "archived" | no |
unpaidBreakHours | number, min 0 or null | no |
billingAddress | string or null | no |
billingStreet | string or null | no |
billingCity | string or null | no |
billingState | string or null | no |
billingPostal | string or null | no |
billingCountry | string or null | no |
paymentTermsOverride | string or null | no |
internalNotes | string or null | no |
colorTag | string or null | no |
linkOrgId | string, min 1 | no |
Response 201: { client, invitationSent, linkedImmediately, inviteUrl, seatQuote }
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
clerkOrgId | string, min 1 | yes |
displayName | string, min 1 | yes |
contactPersonName | string, min 1 | yes |
invoiceEmail | string (email) | yes |
hourlyRate | number, min 0 | yes |
currency | string, min 1, max 8 | yes |
hoursPerDay | number | yes |
shiftStart | string | yes |
shiftEnd | string | yes |
timezone | string, min 1 | yes |
status | "active" or "archived" | yes |
unpaidBreakHours | number, min 0 or null | yes |
billingAddress | string or null | yes |
billingStreet | string or null | yes |
billingCity | string or null | yes |
billingState | string or null | yes |
billingPostal | string or null | yes |
billingCountry | string or null | yes |
paymentTermsOverride | string or null | yes |
internalNotes | string or null | yes |
colorTag | string or null | yes |
clientClerkOrgId | string, min 1 or null | yes |
clerkInvitationId | string, min 1 or null | yes |
invitationStatus | "pending" or "accepted" or "expired" or "revoked" or null | yes |
inviteToken | string (uuid) or null | yes |
seatKind | "full" or "partial" or null | yes |
seatStatus | "pending" or "active" or "ended" or "grandfathered" | yes |
seatAmountCents | integer, min 0 or null | yes |
coversPremium | boolean | yes |
acceptedAt | string or null | yes |
endedAt | string or null | yes |
createdAt | string | yes |
updatedAt | string | yes |
Get a client
GET /api/public/agency/clients/{id}
An unknown id returns 404 { "error": "Client not found" }.
Response 200: { client: Client, summary: ClientSummary }
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
clerkOrgId | string, min 1 | yes |
displayName | string, min 1 | yes |
contactPersonName | string, min 1 | yes |
invoiceEmail | string (email) | yes |
hourlyRate | number, min 0 | yes |
currency | string, min 1, max 8 | yes |
hoursPerDay | number | yes |
shiftStart | string | yes |
shiftEnd | string | yes |
timezone | string, min 1 | yes |
status | "active" or "archived" | yes |
unpaidBreakHours | number, min 0 or null | yes |
billingAddress | string or null | yes |
billingStreet | string or null | yes |
billingCity | string or null | yes |
billingState | string or null | yes |
billingPostal | string or null | yes |
billingCountry | string or null | yes |
paymentTermsOverride | string or null | yes |
internalNotes | string or null | yes |
colorTag | string or null | yes |
clientClerkOrgId | string, min 1 or null | yes |
clerkInvitationId | string, min 1 or null | yes |
invitationStatus | "pending" or "accepted" or "expired" or "revoked" or null | yes |
inviteToken | string (uuid) or null | yes |
seatKind | "full" or "partial" or null | yes |
seatStatus | "pending" or "active" or "ended" or "grandfathered" | yes |
seatAmountCents | integer, min 0 or null | yes |
coversPremium | boolean | yes |
acceptedAt | string or null | yes |
endedAt | string or null | yes |
createdAt | string | yes |
updatedAt | string | yes |
Summary on the get response:
| Field | Type | Required |
|---|---|---|
assignedTalentCount | integer, min 0 | yes |
unbilledHours | number, min 0 | yes |
outstandingCents | integer, min 0 | yes |
outstandingCurrency | string | yes |
Update a client
PATCH /api/public/agency/clients/{id}
Every field is optional. Send only the fields that change.
Request body:
| Field | Type | Required |
|---|---|---|
displayName | string, min 1 | no |
contactPersonName | string, min 1 | no |
invoiceEmail | string (email) | no |
hourlyRate | number, min 0 | no |
currency | string, min 1, max 8 | no |
hoursPerDay | number | no |
shiftStart | string | no |
shiftEnd | string | no |
timezone | string, min 1 | no |
status | "active" or "archived" | no |
unpaidBreakHours | number, min 0 or null | no |
billingAddress | string or null | no |
billingStreet | string or null | no |
billingCity | string or null | no |
billingState | string or null | no |
billingPostal | string or null | no |
billingCountry | string or null | no |
paymentTermsOverride | string or null | no |
internalNotes | string or null | no |
colorTag | string or null | no |
linkOrgId | string, min 1 | no |
Response 200: { client: Client }
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
clerkOrgId | string, min 1 | yes |
displayName | string, min 1 | yes |
contactPersonName | string, min 1 | yes |
invoiceEmail | string (email) | yes |
hourlyRate | number, min 0 | yes |
currency | string, min 1, max 8 | yes |
hoursPerDay | number | yes |
shiftStart | string | yes |
shiftEnd | string | yes |
timezone | string, min 1 | yes |
status | "active" or "archived" | yes |
unpaidBreakHours | number, min 0 or null | yes |
billingAddress | string or null | yes |
billingStreet | string or null | yes |
billingCity | string or null | yes |
billingState | string or null | yes |
billingPostal | string or null | yes |
billingCountry | string or null | yes |
paymentTermsOverride | string or null | yes |
internalNotes | string or null | yes |
colorTag | string or null | yes |
clientClerkOrgId | string, min 1 or null | yes |
clerkInvitationId | string, min 1 or null | yes |
invitationStatus | "pending" or "accepted" or "expired" or "revoked" or null | yes |
inviteToken | string (uuid) or null | yes |
seatKind | "full" or "partial" or null | yes |
seatStatus | "pending" or "active" or "ended" or "grandfathered" | yes |
seatAmountCents | integer, min 0 or null | yes |
coversPremium | boolean | yes |
acceptedAt | string or null | yes |
endedAt | string or null | yes |
createdAt | string | yes |
updatedAt | string | yes |
Delete a client
DELETE /api/public/agency/clients/{id}
Response 200: { ok: true }
Invite a client
POST /api/public/agency/clients/{id}/invite
No request body.
Response 200: { client, invitationSent, linkedImmediately, inviteUrl, seatQuote }
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
clerkOrgId | string, min 1 | yes |
displayName | string, min 1 | yes |
contactPersonName | string, min 1 | yes |
invoiceEmail | string (email) | yes |
hourlyRate | number, min 0 | yes |
currency | string, min 1, max 8 | yes |
hoursPerDay | number | yes |
shiftStart | string | yes |
shiftEnd | string | yes |
timezone | string, min 1 | yes |
status | "active" or "archived" | yes |
unpaidBreakHours | number, min 0 or null | yes |
billingAddress | string or null | yes |
billingStreet | string or null | yes |
billingCity | string or null | yes |
billingState | string or null | yes |
billingPostal | string or null | yes |
billingCountry | string or null | yes |
paymentTermsOverride | string or null | yes |
internalNotes | string or null | yes |
colorTag | string or null | yes |
clientClerkOrgId | string, min 1 or null | yes |
clerkInvitationId | string, min 1 or null | yes |
invitationStatus | "pending" or "accepted" or "expired" or "revoked" or null | yes |
inviteToken | string (uuid) or null | yes |
seatKind | "full" or "partial" or null | yes |
seatStatus | "pending" or "active" or "ended" or "grandfathered" | yes |
seatAmountCents | integer, min 0 or null | yes |
coversPremium | boolean | yes |
acceptedAt | string or null | yes |
endedAt | string or null | yes |
createdAt | string | yes |
updatedAt | string | yes |
Revoke a client invite
POST /api/public/agency/clients/{id}/invite/revoke
No request body.
Response 200: { client, invitationSent }
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
clerkOrgId | string, min 1 | yes |
displayName | string, min 1 | yes |
contactPersonName | string, min 1 | yes |
invoiceEmail | string (email) | yes |
hourlyRate | number, min 0 | yes |
currency | string, min 1, max 8 | yes |
hoursPerDay | number | yes |
shiftStart | string | yes |
shiftEnd | string | yes |
timezone | string, min 1 | yes |
status | "active" or "archived" | yes |
unpaidBreakHours | number, min 0 or null | yes |
billingAddress | string or null | yes |
billingStreet | string or null | yes |
billingCity | string or null | yes |
billingState | string or null | yes |
billingPostal | string or null | yes |
billingCountry | string or null | yes |
paymentTermsOverride | string or null | yes |
internalNotes | string or null | yes |
colorTag | string or null | yes |
clientClerkOrgId | string, min 1 or null | yes |
clerkInvitationId | string, min 1 or null | yes |
invitationStatus | "pending" or "accepted" or "expired" or "revoked" or null | yes |
inviteToken | string (uuid) or null | yes |
seatKind | "full" or "partial" or null | yes |
seatStatus | "pending" or "active" or "ended" or "grandfathered" | yes |
seatAmountCents | integer, min 0 or null | yes |
coversPremium | boolean | yes |
acceptedAt | string or null | yes |
endedAt | string or null | yes |
createdAt | string | yes |
updatedAt | string | yes |
Cover a client's premium
POST /api/public/agency/clients/{id}/cover
charge.status is transitioned, canceled_sponsored, unchanged, or ledger_only. charge.checkoutEnabled is false. charge.reason and charge.parentMonthlyCents are present only for some outcomes.
No request body.
Response 200: { client, charge }
| Field | Type | Required |
|---|---|---|
id | string (uuid) | yes |
clerkOrgId | string, min 1 | yes |
displayName | string, min 1 | yes |
contactPersonName | string, min 1 | yes |
invoiceEmail | string (email) | yes |
hourlyRate | number, min 0 | yes |
currency | string, min 1, max 8 | yes |
hoursPerDay | number | yes |
shiftStart | string | yes |
shiftEnd | string | yes |
timezone | string, min 1 | yes |
status | "active" or "archived" | yes |
unpaidBreakHours | number, min 0 or null | yes |
billingAddress | string or null | yes |
billingStreet | string or null | yes |
billingCity | string or null | yes |
billingState | string or null | yes |
billingPostal | string or null | yes |
billingCountry | string or null | yes |
paymentTermsOverride | string or null | yes |
internalNotes | string or null | yes |
colorTag | string or null | yes |
clientClerkOrgId | string, min 1 or null | yes |
clerkInvitationId | string, min 1 or null | yes |
invitationStatus | "pending" or "accepted" or "expired" or "revoked" or null | yes |
inviteToken | string (uuid) or null | yes |
seatKind | "full" or "partial" or null | yes |
seatStatus | "pending" or "active" or "ended" or "grandfathered" | yes |
seatAmountCents | integer, min 0 or null | yes |
coversPremium | boolean | yes |
acceptedAt | string or null | yes |
endedAt | string or null | yes |
createdAt | string | yes |
updatedAt | string | yes |
