Customers & catalog
Client projects
A client project is the work under an accepted proposal: a title, an agreed amount, dates and milestones for one of your clients. When your client accepts a proposal, Vezmo creates its project for you. You can also create projects yourself.
- A project made by an acceptance starts
IN_PROGRESS, with the proposal's title, description, amount, currency and charge type, the contract's dates, and an emptydeliveryMilestoneslist. Theproposal.acceptedwebhook carries its id asclientProjectId. - Invoices attach to a project with
clientProjectId(see Create invoice). An invoice must use its project's currency. - Projects belong to the user your API key belongs to. Another business's project answers
404. - A project has a mode too: one an accepted proposal starts is of the proposal's mode, and one you create takes your key's. A key sees only its own mode's projects (the other mode's answer
404, with the messageNo such client project: a similar object exists in live mode, but a test mode key was used to make this request.for one your business has in the other mode), and a project links only to a proposal of its own mode.
Every response is the usual envelope: { success, message, data, requestId, timestamp }. amount comes back as a decimal string, such as "1200".
Required API-key permissions
client-project.read— list, retrieve and list deleted projects.client-project.create— create a project.client-project.update— update and restore a project.client-project.delete— delete a project.
All four are in the default client-project permission group, so new keys have them. Send the access token from Create token as Authorization: Bearer <token>.
Create a project
POST /merchant/client-project
| Field | Type | Required | Rules |
|---|---|---|---|
clientId | string | yes | One of your clients. Another id answers 404 “Client not found”. |
proposalId | string | no | One of your proposals, else 404. A free link: the proposal's status and client aren't checked. |
title | string | yes | Not empty. |
description | string | no | Scope and deliverables. |
amount | number | yes | The agreed amount in major units: 0 or more, at most 2 decimals, and at most the equivalent of 1,000,000 USD in its currency. 0 means no budget is set. |
currency | string | no | A supported currency, such as USD (the default), EUR or GBP. |
status | enum | no | DRAFT, OPEN, IN_PROGRESS (the default), ON_HOLD, REVISION, PENDING, COMPLETED or CANCELLED. For your own tracking. |
chargeType | enum | no | FIXED (the default), HOURLY, CUSTOM or RECURRING. |
startDate | date | no | ISO 8601, such as 2026-01-15. |
endDate | date | no | ISO 8601, such as 2026-03-31. |
deliveryMilestones | array of objects | no | At most 100 entries, stored as given, such as [{ "name": "Design", "dueDate": "2026-02-01" }]. |
curl -X POST https://api.vezmo.com/api/v1/merchant/client-project \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientId": "cm2x8k1qz0002abcd1234efgh",
"title": "Website redesign",
"description": "New marketing site + CMS",
"amount": 1200,
"currency": "USD",
"startDate": "2026-01-15",
"endDate": "2026-03-31",
"deliveryMilestones": [{ "name": "Design", "dueDate": "2026-02-01" }]
}'{
"success": true,
"message": "Project created successfully",
"data": {
"id": "cmb1proj0001abcd",
"clientId": "cm2x8k1qz0002abcd1234efgh",
"userId": "cm2usr000001abcd",
"proposalId": null,
"title": "Website redesign",
"description": "New marketing site + CMS",
"amount": "1200",
"currency": "USD",
"status": "IN_PROGRESS",
"chargeType": "FIXED",
"delevery_milestones": [{ "name": "Design", "dueDate": "2026-02-01" }],
"deliveryMilestones": [{ "name": "Design", "dueDate": "2026-02-01" }],
"startDate": "2026-01-15T00:00:00.000Z",
"endDate": "2026-03-31T00:00:00.000Z",
"createdAt": "2026-01-10T09:00:00.000Z",
"updatedAt": "2026-01-10T09:00:00.000Z",
"deletedAt": null
},
"requestId": "9b2f6c1e-4d3a-4f7b-8e21-5c6d7a8b9c0d",
"timestamp": "2026-01-10T09:00:00.000Z"
}Every read of a project carries the milestones twice: as deliveryMilestones, and as delevery_milestones, the field's original misspelled name, kept for older integrations. Use deliveryMilestones. A write still accepts the old name; when both are sent, deliveryMilestones wins.
List projects
GET /merchant/client-project — your live projects, newest first. Deleted projects are left out.
| Field | Type | Required | Rules |
|---|---|---|---|
clientId | string | no | Only this client's projects. |
search | string | no | Matches the title (case-insensitive). |
status | enum | no | One project status, as in Create. |
chargeType | enum | no | FIXED, HOURLY, CUSTOM or RECURRING. |
sortBy | enum | no | amount or createdAt (the default). |
order | enum | no | asc or desc (the default). |
page | integer | no | 1 or more. Default 1. |
limit | integer | no | 1 or more. Default 10, at most 100; a bigger value returns 100. |
curl "https://api.vezmo.com/api/v1/merchant/client-project?status=IN_PROGRESS&page=1&limit=10" \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"success": true,
"message": "Project All retrieved",
"data": {
"data": [
{
"id": "cmb1proj0001abcd",
"title": "Website redesign",
"amount": "1200",
"currency": "USD",
"status": "IN_PROGRESS",
"deliveryMilestones": [{ "name": "Design", "dueDate": "2026-02-01" }],
"…": "the other project fields, as in Retrieve",
"client": { "id": "cm2x8k1qz0002abcd1234efgh", "name": "Acme Ltd", "…": "the full client record" },
"proposal": null
}
],
"pagination": {
"total": 1,
"page": 1,
"limit": 10,
"totalPages": 1,
"nextPage": null,
"prevPage": null
}
},
"requestId": "…",
"timestamp": "…"
}In the list, client and proposal are the full records; Retrieve returns a shorter version of each. Follow pagination.nextPage until it is null.
Retrieve a project
GET /merchant/client-project/{id}
| Field | Type | Required | Rules |
|---|---|---|---|
id | string, path | yes | The project's id. |
curl https://api.vezmo.com/api/v1/merchant/client-project/cmb1proj0001abcd \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"success": true,
"message": "Project retrieved",
"data": {
"id": "cmb1proj0001abcd",
"clientId": "cm2x8k1qz0002abcd1234efgh",
"userId": "cm2usr000001abcd",
"proposalId": "cm2prop00001abcd",
"title": "Website redesign",
"description": "New marketing site + CMS",
"amount": "1200",
"currency": "USD",
"status": "IN_PROGRESS",
"chargeType": "FIXED",
"delevery_milestones": [{ "name": "Design", "dueDate": "2026-02-01" }],
"deliveryMilestones": [{ "name": "Design", "dueDate": "2026-02-01" }],
"startDate": "2026-01-15T00:00:00.000Z",
"endDate": "2026-03-31T00:00:00.000Z",
"createdAt": "2026-01-10T09:00:00.000Z",
"updatedAt": "2026-01-10T09:00:00.000Z",
"deletedAt": null,
"client": {
"id": "cm2x8k1qz0002abcd1234efgh",
"name": "Acme Ltd",
"email": "billing@acme.example.com",
"phone": null,
"company": "Acme",
"country": "US",
"status": "ACTIVE",
"profile_image": null
},
"proposal": {
"id": "cm2prop00001abcd",
"proposalNo": "VP-000123",
"title": "Website redesign",
"amount": "1200",
"currency": "USD",
"status": "ACCEPTED",
"chargeType": "FIXED",
"createdAt": "2026-01-05T09:00:00.000Z"
}
},
"requestId": "…",
"timestamp": "…"
}A deleted project answers 404 here. Find it with List deleted projects.
Update a project
PATCH /merchant/client-project/{id} — send any of the Create fields; only those change.
| Field | Type | Required | Rules |
|---|---|---|---|
id | string, path | yes | The project's id. |
clientId, proposalId | string | no | As in Create: yours, else 404. |
currency | string | no | Can't change once the project has invoices (400). |
amount | number | no | As in Create, checked in the currency the project ends up with. |
startDate, endDate | date | no | ISO 8601. null clears it. |
deliveryMilestones | array of objects | no | Replaces the whole list. |
title, description, status, chargeType | as in Create | no | As in Create. |
curl -X PATCH https://api.vezmo.com/api/v1/merchant/client-project/cmb1proj0001abcd \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "status": "COMPLETED", "endDate": "2026-03-20" }'{
"success": true,
"message": "Project retrieved",
"data": {
"id": "cmb1proj0001abcd",
"status": "COMPLETED",
"endDate": "2026-03-20T00:00:00.000Z",
"deliveryMilestones": [{ "name": "Design", "dueDate": "2026-02-01" }],
"…": "the other project fields, as in Create"
},
"requestId": "…",
"timestamp": "…"
}Delete a project
DELETE /merchant/client-project/{id} — moves the project to the trash. Its invoices are kept. Restore brings it back.
| Field | Type | Required | Rules |
|---|---|---|---|
id | string, path | yes | The project's id. |
curl -X DELETE https://api.vezmo.com/api/v1/merchant/client-project/cmb1proj0001abcd \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"success": true,
"message": "Project retrieved",
"data": {
"id": "cmb1proj0001abcd",
"clientId": "cm2x8k1qz0002abcd1234efgh",
"userId": "cm2usr000001abcd",
"proposalId": null,
"title": "Website redesign",
"description": "New marketing site + CMS",
"amount": "1200",
"currency": "USD",
"status": "IN_PROGRESS",
"chargeType": "FIXED",
"delevery_milestones": [{ "name": "Design", "dueDate": "2026-02-01" }],
"startDate": "2026-01-15T00:00:00.000Z",
"endDate": "2026-03-31T00:00:00.000Z",
"createdAt": "2026-01-10T09:00:00.000Z",
"updatedAt": "2026-03-22T14:05:00.000Z",
"deletedAt": "2026-03-22T14:05:00.000Z"
},
"requestId": "…",
"timestamp": "…"
}Delete and Restore return the project row on its own: no client or proposal, and the milestones only as delevery_milestones.
List deleted projects
GET /merchant/client-project/trash — your deleted projects, with the ids Restore needs. It takes the same query as List projects.
| Field | Type | Required | Rules |
|---|---|---|---|
clientId | string | no | Only this client's projects. |
search | string | no | Matches the title (case-insensitive). |
status | enum | no | One project status, as in Create. |
chargeType | enum | no | FIXED, HOURLY, CUSTOM or RECURRING. |
sortBy | enum | no | amount or createdAt (the default). |
order | enum | no | asc or desc (the default). |
page | integer | no | 1 or more. Default 1. |
limit | integer | no | 1 or more. Default 10, at most 100; a bigger value returns 100. |
curl "https://api.vezmo.com/api/v1/merchant/client-project/trash?page=1&limit=10" \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"success": true,
"message": "Trashed projects retrieved",
"data": {
"data": [
{
"id": "cmb1proj0001abcd",
"title": "Website redesign",
"deletedAt": "2026-03-22T14:05:00.000Z",
"…": "the other project fields, with client and proposal, as in List"
}
],
"pagination": { "total": 1, "page": 1, "limit": 10, "totalPages": 1, "nextPage": null, "prevPage": null }
},
"requestId": "…",
"timestamp": "…"
}Restore a project
PATCH /merchant/client-project/{id}/restore — brings a deleted project back, unchanged.
| Field | Type | Required | Rules |
|---|---|---|---|
id | string, path | yes | The project's id. |
curl -X PATCH https://api.vezmo.com/api/v1/merchant/client-project/cmb1proj0001abcd/restore \
-H "Authorization: Bearer $ACCESS_TOKEN"{
"success": true,
"message": "Project restored",
"data": {
"id": "cmb1proj0001abcd",
"clientId": "cm2x8k1qz0002abcd1234efgh",
"userId": "cm2usr000001abcd",
"proposalId": null,
"title": "Website redesign",
"description": "New marketing site + CMS",
"amount": "1200",
"currency": "USD",
"status": "IN_PROGRESS",
"chargeType": "FIXED",
"delevery_milestones": [{ "name": "Design", "dueDate": "2026-02-01" }],
"startDate": "2026-01-15T00:00:00.000Z",
"endDate": "2026-03-31T00:00:00.000Z",
"createdAt": "2026-01-10T09:00:00.000Z",
"updatedAt": "2026-03-23T08:30:00.000Z",
"deletedAt": null
},
"requestId": "…",
"timestamp": "…"
}Not on the API
Project stats and a project's payments are in the dashboard only. For a project's payments, list its invoices with GET /merchant/invoice?clientProjectId=….
Errors
| Status | When |
|---|---|
400 | A field fails its rules: a bad enum, date, page or limit, a negative or too-large amount, or an unsupported currency. Also “This project already has invoices, so its currency can't be changed.” |
401 / 403 | No token, an expired one, or a key without the route's scope. |
404 | “Project not found”: not yours, or deleted (Retrieve, Update, Delete). “Client not found” or “Proposal not found” for a clientId or proposalId that isn't yours. |
{
"success": false,
"message": "Project not found",
"errors": [{ "code": "http.404", "message": "Project not found", "statusCode": 404 }],
"data": null,
"requestId": "…",
"timestamp": "…"
}See Errors for the error body.