Opens in a new tab

Automation and webhooks

Your site, CRM, Zap, or chat agent can create a client and a draft. A human still previews and sends. Nothing auto-sends.

This is Team+. You need at least one company in the workspace.

When to use this

SituationUse Incoming Webhooks
A website or form should save a leadYes — POST from your server, never from the browser
Zapier / Make / n8n should create a client or draftYes
A chat agent (Telegram bot, etc.) should open a draftYes — the agent POSTs; chat apps themselves only send alerts
People already live in a CRMPrefer CRM sync. Use a webhook only for new form leads

Activate an endpoint

  1. Open Integrations → Incoming Webhooks.
  2. Create an endpoint. Give it a name you will recognize later (for example “Website form”).
  3. Copy and store these two values in your server env (or Zap / agent secrets). Never put them in frontend JavaScript, a public repo, or a form builder’s client-side webhook field.
ItemWhat it is
URLPOST {app}/api/webhooks/{token}
Signing secretUsed as Authorization: Bearer <secret> or X-Webhook-Signature: sha256=<hex>
  1. Keep the endpoint Active. Disable it if you rotate the secret or retire the form.
  2. Send a test POST (curl below, or the sample in the card). A client named “Test Lead” should appear under Clients.

Optional: copy a template ID from Templates (key icon) if proposal.draft should open a priced pack instead of a blank draft. Visitors cannot pass a template ID in a public form field — keep it on the server.

If a CRM card has Push webhook clients to CRM on, these upserts also add/update the contact there. Off by default. Never deletes.

Auth

Bearer (simplest for a trusted server, Zap, or agent):

POST /api/webhooks/{token}
Authorization: Bearer <signingSecret>
Content-Type: application/json

HMAC (when the secret should not travel as a bearer token):

POST /api/webhooks/{token}
X-Webhook-Signature: sha256=<hex>
Content-Type: application/json

hex is lowercase HMAC-SHA256(signingSecret, rawRequestBody). The header may include a sha256= prefix.

Events

typeResult
client.upsertCreate or update a Client. Match on external_id when you send one
client.createSame shape as upsert — create path
client.updateSame shape — requires client.external_id
proposal.draftUpsert optional client + open a draft (blank, or from template_id / template_key)

Fields you can send

Top-level JSON only. InProQu does not accept a raw HTML form post — your server, Zap, or agent maps fields into this shape.

Always

FieldRequiredNotes
typeYesOne of the events above

client object

Required for client.*. Optional on proposal.draft (omit only if you will attach the client later by hand).

FieldRequiredNotes
nameYes (when client is sent)Person’s name
emailNoValid email when present. Needed later for in-app send
companyNoOrganization, not the person
phoneNo
notesNoExtra context (message, source, budget)
external_idNoYour stable ID (CRM id, form lead id). Same value updates the same client. Required for client.update

These land on the Client record. On the document they fill {{client_name}}, {{client_email}}, {{client_company}}, and {{client_phone}}.

proposal object

Required for proposal.draft.

FieldRequiredNotes
titleYesDocument title
project_nameNoShown as the project name on the draft
template_idNoWorkspace or catalog template cuid from Templates (key icon)
template_keyNoOptional catalog layout key. Omit both for a blank draft

Do not invent other keys. Pricing, billing, and body copy stay in the template or the builder — the webhook does not set line items.

Sample payloads

Client only

{
  "type": "client.upsert",
  "client": {
    "name": "Alex Rivera",
    "company": "Northwind Labs",
    "email": "[email protected]",
    "phone": "+1 555 0100",
    "notes": "Needs a brochure site by Q3",
    "external_id": "crm-123"
  }
}

Client + draft from a template

{
  "type": "proposal.draft",
  "client": {
    "name": "Alex Rivera",
    "company": "Northwind Labs",
    "email": "[email protected]",
    "external_id": "crm-123"
  },
  "proposal": {
    "title": "Website rebuild quote",
    "project_name": "Marketing site",
    "template_id": "clxxxxxxxx"
  }
}

Success response

Client only: ok, clientId, urls.client.

Draft: ok, proposalId, clientId, optional templateId, and:

URLWho sees it
urls.builderYour team — open and finish the draft
urls.previewYour team — preview before share
urls.clientYour team — the Client record

Paste urls.builder into Slack or the agent chat. Do not put builder URLs on a public thank-you page.

Quick curl test

curl -sS -X POST "$INPROQU_WEBHOOK_URL" \
  -H "Authorization: Bearer $INPROQU_WEBHOOK_SECRET" \
  -H "Content-Type: application/json" \
  -d '{"type":"client.upsert","client":{"name":"Test Lead","email":"[email protected]"}}'

Sample prompts

Copy into Cursor, Claude, or your agent. Replace the CAPS placeholders.

Wire an existing form (client only)

Connect my website contact form to InProQu Incoming Webhooks.

Requirements:
- On submit, call MY server endpoint (never call InProQu from the browser).
- Server POSTs JSON to INPROQU_WEBHOOK_URL with Authorization: Bearer INPROQU_WEBHOOK_SECRET.
- Body type "client.upsert" with client.name (required), plus client.email, client.company, client.phone, client.notes from the form when present.
- Keep the signing secret in env vars only.
- Return a generic success to the visitor; log InProQu errors server-side.
- Do not expose builder URLs or webhook secrets to the frontend.

My stack: DESCRIBE_STACK (e.g. Next.js App Router / Cloudflare Worker / PHP).
Form fields: LIST_FIELD_NAMES.
Webhook URL and secret are already in env as INPROQU_WEBHOOK_URL and INPROQU_WEBHOOK_SECRET.

Form → client + draft from a template

When someone submits my “Request a proposal” form, create an InProQu client and open a draft document from a template.

- Server-side only POST to Incoming Webhooks.
- type: "proposal.draft"
- Map form fields to client.name, client.email, client.company, client.phone, client.notes.
- proposal.title from company or a default like "Website proposal for {company}".
- proposal.template_id = TEMPLATE_ID_FROM_INPROQU_TEMPLATES.
- Auth: Bearer INPROQU_WEBHOOK_SECRET.
- On success, notify MY_TEAM_CHANNEL with urls.builder from the JSON response (internal only).
- Visitor only sees a thank-you message.

Stack: DESCRIBE_STACK.

Chat agent tool

Add a tool that creates an InProQu client and draft via Incoming Webhooks.

- POST JSON to INPROQU_WEBHOOK_URL
- Header Authorization: Bearer INPROQU_WEBHOOK_SECRET
- type "proposal.draft" with client.name (required), client.email, client.company, client.phone, client.notes, client.external_id
- proposal.title, proposal.project_name, and proposal.template_id from env TEMPLATE_ID
- On success, reply in chat with urls.builder only (for the human on my team)
- Never print the signing secret or put builder links in a customer-facing message

After a draft appears

  1. Open urls.builder.
  2. Check company, client, pricing, How to pay.
  3. Preview Website and Document.
  4. Share — that is the first time the client sees it.

Outbound (document events → other tools)

Team+ can also POST view / share / accept (and related events) to a Zapier catch hook, Make, n8n, or any HTTPS URL you paste.

Payloads include source: "inproqu". Use this for ops (Slack is often simpler via the messaging card). Outbound does not create clients — that is incoming.

If something fails

Status / symptomMeaning
401Bad Bearer secret or HMAC
402Plan does not allow inbound webhooks
422 company_requiredAdd a company profile first
422 invalid payloadFix JSON shape / required fields (client.name, or proposal.title on drafts)
429Rate limited — retry later
Template errorsMissing or locked template ID — use a valid Templates ID
Two drafts from one submitYour form or Zap fired twice. Send one POST per lead

Related: How the workspace fits together · Start a document · Add clients.