Qwilr API

Qwilr provides access to API to both build Qwilr pages as well as subscribe to page events and integrate with other systems. This article will cover how to get started with our API features. For more information on Qwilr's API you can also review our API documentation pages here: API Docs and Reference Site


In this article:

From a Template

From Saved Blocks


Setting Up API Access

Note: API Access is available on Starter, Growth, and Scale Plans.

Page creation is charged on a per-page basis. Accounts that have access to Qwilr's API can buy bundles of API pages. More information on pricing and setup for page creation can be found here.

Step 1. Head over to your API settings by logging into your Qwilr account and going to Account settings on the left-hand sidebar of your Qwilr dashboard at the bottom.


Step 2. Select Qwilr API on the left, then create an API Access Token by adding a clear description of what the access token is for, then copy the token immediately and store it somewhere secure. This is the only time you'll be able to view it. If you lose it, you'll need to generate a new one.


Important info: Your access token grants full access to your Qwilr account. Don’t include it in your codebase, publish it to GitHub, or share it in public spaces.


Step 3. BearerAuth

All API requests must include your access token as a Bearer token in the Authorization        header:

Authorization: Bearer YOUR_ACCESS_TOKEN

Content-Type: application/json

All requests must be sent over HTTPS to:

https://api.qwilr.com/v1

Rate Limits: The API allows up to 120 requests per minute from a single IP address. Requests exceeding this limit will receive a 429 Too Many Requests    response.


Creating Pages

Pages are created by sending a POST        request to /pages       . There are two methods for building a page, each suited to different use cases:


From a Template

  • Allows you to create pages based on Templates in Qwilr that you design
  • Use tokens as dynamic placeholders
  • All blocks are fixed; layout is pre-set
  • Text, images, and maps can be personalized
These are best utilized when content structure and proposal layout is overall consistent and where your quote block does not need dynamic information inserted into it specifically.

From Saved Blocks

  • Programmatically include, exclude, or reorder saved blocks
  • Supports the defining and manipulating line items in the Quote block
  • More flexibility, however, utilizing a more complex setup
  • Requires block IDs (retrievable via GET /blocks/saved       )
This works better for constructing different blocks dynamically from different user categories and offerings. Layouts of content and which content is included can be more refined.

Important: If your page needs Quote block pricing modified or dynamic, you must use the Saved Blocks approach. Dynamic Quote blocks are not supported via templates.

Creating Pages from a Template


Step 1. Create your template in Qwilr. For more guidance on this, go to our Templates help doc. After your template is created you can get the template ID from the URL in our editor. Example: app.qwilr.com/#/page/your-template-id


Step 2. Personalize your template with tokens in the editor. API reference keys for substitutions will use underscores for spaces and lower case letters. Example: a token named "Client Name" becomes client_name       


You can find your API references keys for tokens in your account when editing a template and going to the 'tokens' button, selecting account, and clicking on the three dot menu beside the token to 'Copy API reference'.

Post Request Example

// POST https://api.qwilr.com/v1/pages
{
  "templateId": "your-template-id-here",
  "name": "Proposal for Acme Corp",
  "published": true,
  "substitutions": {
    "client_name": "Acme Corp",
    "proposal_value": "$12,000",
    "expiry_date": "30 April 2026"
  },
  "metadata": {
    "crm_deal_id": "deal_9834",
    "sales_rep": "jane.smith"
  },
  "tags": ["enterprise", "q2-2026"]
}

For reference in the above:

  • name: string

    Title of the page, visible as the browser page title.

  • published: boolean

    Whether the page is publically available; false        means the page will be in Draft status. Default is false       .

  • substitutions: object

    Mapping of token API reference keys to substitution values used throughout the page. The values can be overwritten if the same keys are defined in the block-level substitutions.

  • metadata: object

    Data you provide, that will be returned as part of all Webhooks.

  • tags: array of strings

    The tags for your page. Tags are case-sensitive.

  • ownerId: string(Id)^[a-z0-9]{24}$

    ID of the user that should own the page. If not specified, the owner of the access token will be the owner of the page.


Create Pages from Saved Blocks


Step 1. Build your saved blocks by creating a Template in Qwilr, adding your tokens while in the template editor, and then saving those blocks individually with the included tokens.


Step 2. Get block IDs by either copying each block ID from the Blocks library (three-dot menu → "Copy Block ID"), or call GET /blocks/saved       to retrieve all IDs programmatically.


Post Request Example

// POST https://api.qwilr.com/v1/pages
{
  "blocks": [
    {
      "id": "your-intro-block-id",
      "substitutions": {
        "client_name": "Acme Corp"
      }
    },
    {
      "id": "your-quote-block-id",
      "quoteSettings": {
        "currency": "USD",
        "selectionType": "single"
      },
      "quoteSections": [
        {
          "displayMode": "table",
          "lineItems": [
            {
              "type": "fixedCost",
              "description": "Platform licence (annual)",
              "unitPrice": 9600,
              "quantity": 1,
              "billingSchedule": "annual"
            }
          ]
        }
      ]
    }
  ],
  "name": "Acme Corp — Annual Proposal",
  "published": true,
  "substitutions": {
    "client_name": "Acme Corp"
  },
  "metadata": { "crm_deal_id": "deal_9834" },
  "tags": ["enterprise"],
  "ownerId": "6ee0f841f3cc8900090d82dc"
}

Token formatting tip: Tokens must be formatted in the Qwilr editor before page creation — you cannot pass HTML or markdown via the API. Line breaks can be passed using \n       in your substitution values.

Successful Response

A successful creation returns an HTTP 201       with a JSON object containing the new page's ID, public URL, collaborate link, and PDF download URL. Store the id       field as you'll need it for webhooks and future API calls.


Updating a Page

After creation, the only property you can update via the API is whether a page is published (live) or unpublished (draft)


Assigning Page Ownership

By default, the user associated with the access token owns new pages. To assign ownership to another team member, include their ownerId       in the request. You can retrieve user IDs by calling GET /users      


Webhooks


Webhooks let Qwilr notify your systems in real time when something happens to a page. Instead of polling the API, you register a URL and Qwilr will send an HTTP POST       to it whenever a subscribed event fires.


Subscribing to an Event

// POST https://api.qwilr.com/v1/webhooks
{
  "event": "pageAccepted",
  "targetUrl": "https://your-server.com/webhooks/qwilr"
}

The response returns a subscription id      which you will want to save if you need to cancel the subscription later.


Available Events

Event When it fires
pageAccepted       All required signers have accepted the page
pagePartiallyAccepted       At least one signer has accepted, but not all (e.g. 1 of 3 signatures collected)
pagePreviewAccepted       A preview acceptance was completed (useful for testing)
pageFirstViewed       The page was opened for the first time
pageViewed       The page was opened (fires on every view)
pageSetLive       The page was published / set to Live status
pageRevivedLive       A previously declined, expired, or draft page was re-published

Webhook Payload

Each webhook callback includes the triggering event type, the pageId      , and any metadata       you attached when the page was created. To get the full page details, call GET /pages/{pageId}      .

// Example pageAccepted payload received at your targetUrl
{
  "event": "pageAccepted",
  "pageId": "abc123def456",
  "metadata": {
    "crm_deal_id": "deal_9834",
    "sales_rep": "jane.smith"
  },
  "acceptedAt": "2026-04-16T10:22:00Z"
}

Responding to Callbacks

Your endpoint must return an HTTP 200       response. If it doesn't (due to a timeout after 30 seconds, or an error response), Qwilr will retry the callback once more. If both attempts fail, the callback is dropped.


Security Recommendations

Qwilr webhooks don't include a cryptographic signature by default. To secure your endpoint:

  • Obfuscate your URL: use a UUID or random string in your callback URL so it can't be guessed
  • Use metadata secrets: include a shared secret in the metadata       field when creating pages, and verify it when the webhook arrives
  • Allowlist Qwilr's IP: Qwilr sends callbacks from 13.237.241.97      

Managing Subscriptions

To list all your active webhook subscriptions: GET /webhooks      

To cancel a subscription: DELETE /webhooks/{subscriptionId}      


Troubleshooting Page Creation


HTTP Error Reference

Status Meaning Common cause
400 Bad Request Malformed JSON, missing required fields, or invalid values in the request body
401 Unauthorised Missing or invalid access token in the Authorization       header
403 Forbidden Token is valid but lacks permission (e.g. API not enabled for your account)
404 Not Found The template ID or block ID doesn't exist or belongs to a different account
422 Unprocessable Request structure is valid JSON, but fails Qwilr's business logic validation
429 Rate Limited More than 120 requests per minute from one IP address

Common Issues: Templates


Tokens appear blank on the created page

  • Check the following:
    • Use the API reference key, not the display name. In the editor, a token might be called "Client Company" but its API key could be client_company      . Go to the Token manager in your editor to confirm the exact key.
    • Values must be strings. Pass all substitution values as strings (e.g. "12000"       not 12000      ), unless you're using repeating tokens which require an array.
    • Substitutions can be page-level or block-level. Values in the top-level substitutions       object apply to the entire page; values in blocks[n].substitutions       apply to that specific block and take precedence.

Special characters appear as &      , '       etc.

Characters are being HTML-encoded somewhere in your pipeline. Check that you're not double-encoding values before sending. Also ensure you're using the modern "blue bubble" token style in the editor, not the legacy {{ }}       double-brace style, which has a known encoding issue.


Template ID returns 404

Confirm the ID is copied from the URL when in template-edit mode. Also verify the access token used belongs to the same account that owns the template.


Common Issues: Saved Blocks


Block ID returns 404

Use GET /blocks/saved       to retrieve a fresh list of valid IDs. IDs can change if a block is deleted and recreated. Confirm you're not mixing template IDs and block IDs in the same request.


Quote block doesn't appear or shows incorrect data

The Quote block can only have tokens in the text area at the top of the Quote block. When it comes to the entire contents of the quote tables/line items/etc., these are replaced by the quoteSections       data in your API request. Any formatting applied in the editor to the quote block will be overwritten. Line item types must be either fixedCost       or text      ; the variable       type is not currently supported.


Request too large error

The API has a maximum request size of 200KB. This is most commonly hit when passing large substitution values (e.g. long blocks of text). Trim content where possible, and avoid sending base64-encoded image data. Use public image URLs instead.


Common Issues: Webhooks


Not receiving callbacks at all

  • Confirm the subscription was successfully created (GET /webhooks       to list active subscriptions)
  • Check that your targetUrl       is publicly accessible. Localhost URLs won't work in production
  • Ensure you're not testing page views while logged into Qwilr. Internal views are filtered out by default

Receiving callbacks multiple times

Your endpoint may not be returning an HTTP 200       response, causing Qwilr to retry. Check that your handler always returns 200       even if you process the event asynchronously. Ensure you haven't accidentally registered the same subscription multiple times.


Frequently Asked Questions


Are there any limits on API Usage?

There’s a rate limit on API calls that amounts to 120 requests/minute from a single IP address. Calls above this will fail, with a 429 response


Can I update an existing Qwilr page via the API?

The only update currently supported is toggling the page's published status (live or draft) via PUT /pages/{pageId}      . Updating token values or page content after creation is not yet supported but is on the roadmap.

Can I accept a Qwilr page through the API?

No. Acceptance involves a multi-step process including a signature and cannot be automated via the API. Pages must be accepted by the recipient through the Qwilr page interface.

Can I download a PDF of the page via the API?

Yes. The pdfUrl       is returned in the create page response. However, the PDF may not be immediately ready. Wait at least 5 minutes after page creation before retrieving it. For accepted pages, the auditTrailPdf       URL is available immediately through the get page endpoint with the acceptance       expand option.

Can I use the Quote block with templates?

Editing and assigning values in a Quote block is only supported when using the Saved Blocks approach. If you use a template that contains a Quote block, the pricing data cannot be dynamically set via the API.

Can I pass HTML or rich text through the API?

No. HTML passed through substitution values will be escaped and displayed as plain text. Formatting must be applied to tokens in the Qwilr editor before page creation. You can use \n       for line breaks, and Unicode characters for symbols like bullets (•, ★, ✦).

What are the page statuses returned by the API?

API status Meaning
draft       Page is unpublished
live       Published and accessible (includes "Pending" state)
accepting       Partially accepted — at least one, but not all required signatures collected
accepted       Fully accepted by all required signers
declined       Declined by the recipient

Can I search or list all pages in my account?

Yes. GET /pages   returns a paginated, newest-first list of your pages, filterable by status, tags, folder, or owner. See the API Reference for parameters and response details.


What is the maximum API request size?

Requests are limited to 200KB. This limit is most commonly reached when sending large token values. If you encounter a "request entity too large" error, review the size of your substitution data and consider using public image URLs rather than embedding image data directly.

Yes - you can set a page expiry via the public API when creating a page, and also update it using the update page endpoint.

Can I use repeating tokens inside 2-column or accordion widgets?

No. Repeating tokens are not supported inside 2-Column or Accordion widgets. However, you can place a Columns widget or an Accordion inside a repeating token, and a new instance will be created for each item in the data array.

Does the API support right-to-left languages?

Unfortunately, as with the main Qwilr app and pages, the API doesn’t enable right-to-left language content


What limits do we have for the metadata sent to the API?

Metadata from 3rd party systems is sent as a JSON object within the create page request. This means that it needs to be valid JSON that fits in the 200kb size limit.


Can I choose which Creators can edit/view the page via the API?

The API doesn’t have the ability to change the page permissions, which users or teams can edit or view.


Can the payment options be specified through the API?

We don’t have the page’s payment options directly available through the API. However multiple accept blocks can be used, set with the different payment options and then selected which to use when creating the page through the API.

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us