Kwill

Overview & events

The webhook events Kwill sends, with full example payloads for each.

Webhooks let you receive real-time HTTP callbacks when things happen in your Kwill workspace. Each delivery is a POST to your endpoint with a JSON body describing the event.

Register an endpoint via the dashboard (Settings → Developer → Webhooks) or with POST /webhooks. See the API reference for the full parameter list.


Common envelope

Every webhook payload shares the same outer shape:

{
  "id": "evt_01hxyz123456",
  "type": "invoice.paid",
  "created_at": "2024-11-15T14:32:00Z",
  "data": {
    "object": { /* the resource, as of when the event happened */ }
  }
}
FieldDescription
idUnique event ID. Also sent as X-Kwill-Event-Id. Use for deduplication.
typeThe event type string.
created_atISO 8601 timestamp of when the event happened.
data.objectThe resource that changed, with the same fields the API returns for it (Client list fields, Project, Invoice, Proposal).

Events

client.created

Fires when a new client is created (via API or dashboard).

{
  "id": "evt_3f2a8b1c4d5e6f70",
  "type": "client.created",
  "created_at": "2024-03-15T14:22:00Z",
  "data": {
    "object": {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Jane Smith",
      "email": "jane@acme.example",
      "company": "Acme Corp",
      "created_at": "2024-03-15T14:22:00Z"
    }
  }
}

project.created

Fires when a new project is created.

{
  "id": "evt_3f2a8b1c4d5e6f70",
  "type": "project.created",
  "created_at": "2024-03-15T14:22:00Z",
  "data": {
    "object": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef0123456789",
      "client_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Brand Refresh",
      "type": "fixed",
      "status": "active",
      "description": "Full brand identity refresh for Q2 launch.",
      "budget": 12000,
      "start_date": "2024-03-01",
      "end_date": "2024-05-31",
      "stages": [
        {
          "id": "stg-1",
          "name": "Discovery",
          "status": "done"
        },
        {
          "id": "stg-2",
          "name": "Design",
          "status": "active"
        }
      ],
      "created_at": "2024-03-15T14:22:00Z",
      "client": {
        "name": "Jane Smith",
        "email": "jane@acme.example",
        "company": "Acme Corp"
      }
    }
  }
}

project.stage_changed

Fires when a project moves to a different stage. Alongside data.object (the project), it carries data.stage and data.previous_stage, each { id, name } or null when the project is not in any stage (stage is null once the last stage is finished or the project is completed).

{
  "id": "evt_3f2a8b1c4d5e6f70",
  "type": "project.stage_changed",
  "created_at": "2024-03-15T14:22:00Z",
  "data": {
    "object": {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef0123456789",
      "client_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Brand Refresh",
      "type": "fixed",
      "status": "active",
      "description": "Full brand identity refresh for Q2 launch.",
      "budget": 12000,
      "start_date": "2024-03-01",
      "end_date": "2024-05-31",
      "stages": [
        {
          "id": "stg-1",
          "name": "Discovery",
          "status": "done"
        },
        {
          "id": "stg-2",
          "name": "Design",
          "status": "active"
        }
      ],
      "created_at": "2024-03-15T14:22:00Z",
      "client": {
        "name": "Jane Smith",
        "email": "jane@acme.example",
        "company": "Acme Corp"
      }
    },
    "stage": {
      "id": "stg-2",
      "name": "Design"
    },
    "previous_stage": {
      "id": "stg-1",
      "name": "Discovery"
    }
  }
}

invoice.created

Fires when a new invoice is created (API or dashboard).

{
  "id": "evt_3f2a8b1c4d5e6f70",
  "type": "invoice.created",
  "created_at": "2024-03-15T14:22:00Z",
  "data": {
    "object": {
      "id": "b2c3d4e5-f6a7-8901-bcde-f01234567890",
      "client_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "project_id": "a1b2c3d4-e5f6-7890-abcd-ef0123456789",
      "invoice_number": "INV-0042",
      "line_items": [
        {
          "id": "li-1",
          "description": "Design work",
          "quantity": 40,
          "rate": 125,
          "amount": 5000
        }
      ],
      "discount": 0,
      "tax_rate": 0,
      "currency": "USD",
      "subtotal": 5000,
      "total": 5000,
      "due_date": "2024-04-15",
      "note": null,
      "status": "draft",
      "paid_at": null,
      "viewed_at": null,
      "created_at": "2024-03-15T14:22:00Z",
      "client": {
        "name": "Jane Smith",
        "email": "jane@acme.example"
      }
    }
  }
}

invoice.paid

Fires when the client pays an invoice via the client portal.

{
  "id": "evt_3f2a8b1c4d5e6f70",
  "type": "invoice.paid",
  "created_at": "2024-03-15T14:22:00Z",
  "data": {
    "object": {
      "id": "b2c3d4e5-f6a7-8901-bcde-f01234567890",
      "client_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "project_id": "a1b2c3d4-e5f6-7890-abcd-ef0123456789",
      "invoice_number": "INV-0042",
      "line_items": [
        {
          "id": "li-1",
          "description": "Design work",
          "quantity": 40,
          "rate": 125,
          "amount": 5000
        }
      ],
      "discount": 0,
      "tax_rate": 0,
      "currency": "USD",
      "subtotal": 5000,
      "total": 5000,
      "due_date": "2024-04-15",
      "note": null,
      "status": "paid",
      "paid_at": "2024-03-15T14:22:00Z",
      "viewed_at": null,
      "created_at": "2024-03-15T14:22:00Z",
      "client": {
        "name": "Jane Smith",
        "email": "jane@acme.example"
      }
    }
  }
}

proposal.signed

Fires when a client accepts (signs) a proposal.

{
  "id": "evt_3f2a8b1c4d5e6f70",
  "type": "proposal.signed",
  "created_at": "2024-03-15T14:22:00Z",
  "data": {
    "object": {
      "id": "c3d4e5f6-a7b8-9012-cdef-012345678901",
      "client_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "project_id": null,
      "title": "Website Redesign Proposal",
      "scope": "Full redesign of the marketing site.",
      "deliverables": [
        "Wireframes",
        "Design mockups",
        "Handoff assets"
      ],
      "line_items": [
        {
          "id": "li-1",
          "description": "Design work",
          "quantity": 40,
          "rate": 125,
          "amount": 5000
        }
      ],
      "discount": 0,
      "tax_rate": 0,
      "currency": "USD",
      "total": 5000,
      "start_date": "2024-04-01",
      "expiry_date": "2024-04-30",
      "status": "signed",
      "viewed_at": "2024-03-16T09:00:00Z",
      "signed_at": "2024-03-15T14:22:00Z",
      "declined_at": null,
      "created_at": "2024-03-15T14:22:00Z",
      "client": {
        "name": "Jane Smith",
        "email": "jane@acme.example"
      }
    }
  }
}

proposal.declined

Fires when a client declines a proposal.

{
  "id": "evt_3f2a8b1c4d5e6f70",
  "type": "proposal.declined",
  "created_at": "2024-03-15T14:22:00Z",
  "data": {
    "object": {
      "id": "c3d4e5f6-a7b8-9012-cdef-012345678901",
      "client_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "project_id": null,
      "title": "Website Redesign Proposal",
      "scope": "Full redesign of the marketing site.",
      "deliverables": [
        "Wireframes",
        "Design mockups",
        "Handoff assets"
      ],
      "line_items": [
        {
          "id": "li-1",
          "description": "Design work",
          "quantity": 40,
          "rate": 125,
          "amount": 5000
        }
      ],
      "discount": 0,
      "tax_rate": 0,
      "currency": "USD",
      "total": 5000,
      "start_date": "2024-04-01",
      "expiry_date": "2024-04-30",
      "status": "declined",
      "viewed_at": "2024-03-16T09:00:00Z",
      "signed_at": null,
      "declined_at": "2024-03-15T14:22:00Z",
      "created_at": "2024-03-15T14:22:00Z",
      "client": {
        "name": "Jane Smith",
        "email": "jane@acme.example"
      }
    }
  }
}

webhook.test

A synthetic event sent when you click Send test event in the dashboard or call POST /webhooks/{id}/test. Its data.object is empty — it does not reflect real data. Use it to verify your endpoint is reachable and your signature verification is working.

{
  "id": "evt_3f2a8b1c4d5e6f70",
  "type": "webhook.test",
  "created_at": "2024-03-15T14:22:00Z",
  "data": {
    "object": {}
  }
}

Test deliveries get exactly one attempt — failure is final and does not count against the endpoint's failure streak.

On this page