Gipo
Resources

Clients

Full CRUD over clients. Projects link to a client by name via the project's clientName field.

A client is a lightweight directory entry — a name plus a rich-text descriptionHtml — shared across the organization. Projects link to a client through the project's free-text clientName: when you create or update a project with a clientName, a matching client is found-or-created automatically and the project's clientId is set. Renaming a client updates the linked projects' label; deleting one keeps the projects and just clears their clientId.

Endpoints

MethodPathNotes
GET/api/v1/clientsList. Supports query, offset paging.
POST/api/v1/clientsCreate. Org owner or admin only. Accepts Idempotency-Key.
GET/api/v1/clients/{id}Returns the client plus its linked projects.
PATCH/api/v1/clients/{id}Update. Org owner or admin only.
DELETE/api/v1/clients/{id}Delete. Org owner or admin only.
GET/api/v1/clients/{id}/contactsList the client's portal contacts.
POST/api/v1/clients/{id}/contactsInvite a contact. Accepts Idempotency-Key.
GET/api/v1/clients/{id}/contacts/{contactId}Retrieve a contact.
PATCH/api/v1/clients/{id}/contacts/{contactId}Set the contact's canOpenRequests permission.
POST/api/v1/clients/{id}/contacts/{contactId}/resendRe-send a pending or expired invite.
POST/api/v1/clients/{id}/contacts/{contactId}/revokeEnd a contact's access.

Client model

{
  "id": "9b1c…",
  "organizationId": "1f3c…",
  "name": "Acme Corp",
  "phone": null,
  "status": "active",
  "descriptionHtml": "<p>Primary retainer client.</p>",
  "createdAt": "2026-05-01T08:30:00.000Z",
  "updatedAt": "2026-05-19T14:22:11.000Z"
}
FieldTypeNotes
namestring1–150 chars.
descriptionHtmlstringRich-text HTML, ≤ 50,000 chars. Sanitized server-side; defaults to "".
phonestring | nullReserved; not editable via this API.
statusenumactive · inactive. Reserved; defaults to active.

Listing clients

GET /api/v1/clients?query=acme&limit=25&offset=0
ParamTypeNotes
limitint 1–100Default 50.
offsetint ≥ 0Default 0.
querystringCase-insensitive search over name.

Returns { data, total }, newest first.

Creating a client

POST /api/v1/clients
{
  "name": "Acme Corp",
  "descriptionHtml": "<p>Primary retainer client.</p>"
}

Required:

FieldTypeConstraint
namestring1–150 chars

Optional:

FieldTypeConstraint
descriptionHtmlstringRich-text HTML, ≤ 50,000 chars. Sanitized server-side.

Response: 201 Created with the new client.

Retrieving a client

GET /api/v1/clients/{id}

Returns the client plus projectsCount and a linkedProjects array (id, name, status, createdAt), newest first.

Updating a client

PATCH /api/v1/clients/{id}
{ "name": "Acme Incorporated", "descriptionHtml": "<p>Updated.</p>" }

All fields are optional. descriptionHtml is rich-text HTML, sanitized server-side; send an empty string to clear it. Renaming a client also updates the clientName label on every linked project. Org owner or admin only.

Deleting a client

DELETE /api/v1/clients/{id}

Returns { "data": { "success": true } }. Linked projects are kept — their clientId is set to null (the clientName text is preserved). Org owner or admin only.

Client portal contacts

A contact is a person invited to follow the client's projects in the client portal. Every contact endpoint is org owner or admin only (403 otherwise). There is no DELETE: revoking is the only way to end access.

{
  "id": "4e7a…",
  "clientId": "9b1c…",
  "email": "[email protected]",
  "name": "Maria",
  "status": "active",
  "accountEmail": "[email protected]",
  "accountDeleted": false,
  "expiresAt": "2026-10-20T12:00:00.000Z",
  "lastAccessAt": "2026-10-13T09:41:00.000Z",
  "canOpenRequests": false,
  "createdAt": "2026-10-13T12:00:00.000Z"
}
FieldTypeNotes
emailstringThe invited email, lowercase.
statusenuminvited · expired · active · revoked. expired is an invited contact past expiresAt.
accountEmailstring | nullThe current email of the contact's account. It can differ from email.
accountDeletedbooleantrue when the contact was revoked because the person deleted their account.
expiresAtISO dateThe invite link expires 7 days after the last send.
lastAccessAtISO date | nullLast portal visit, updated at most once per hour. Read-only.
canOpenRequestsbooleanWhether the contact can open client requests in the portal. false by default.

Inviting a contact

POST /api/v1/clients/{id}/contacts
{ "email": "[email protected]", "name": "Maria" }

email is required; name (≤ 150 chars) and canOpenRequests are optional. A new contact without canOpenRequests cannot open requests; a re-invite without it keeps the current value. Returns 201 with the contact, and the person gets the invite email. Inviting a revoked contact invites them again; inviting a pending one re-sends the invite. Sending lastAccessAt returns 422.

StatusWhen
400The client is inactive.
409The email belongs to a member of the organization, or is already an active contact.
429More than 100 invites and re-sends in 24 hours for the organization.

Re-sending and revoking

POST …/resend sends a new link and invalidates the old one. It works on invited and expired contacts only (400 otherwise), at most once a minute and 5 times a day per contact.

POST …/revoke ends access on the contact's next request. Revoking a revoked contact changes nothing. Both return { data } with the updated contact.

Setting the request permission

PATCH /api/v1/clients/{id}/contacts/{contactId}
{ "canOpenRequests": true }

canOpenRequests is the only writable field and is required. Returns { data } with the updated contact. Any other body gets 422.

On this page