SAQ Integration Guide

Build SAQ compliance workflows into your application using the Preczn API.

This guide walks through the SAQ management workflows available through the Preczn API. Since SAQ operations are headless, you build the UI and user experience on your platform while using these APIs to create, sign, and manage SAQ records.

📘

API Reference

This guide covers workflows and concepts. For full endpoint documentation including request/response schemas, see the SAQ API Reference.

Prerequisites

Upload an SAQ Template

Before you can generate and sign SAQs, your platform needs an SAQ template PDF uploaded. This is a one-time setup step per environment (test and live).

POST /v1/saq/template/upload
Content-Type: multipart/form-data

Upload your SAQ-A template as a PDF file (max 15MB). Preczn uses this template as the base document when generating completed SAQ PDFs at signing time.

Your template can include hardcoded data and pre-filled responses for requirements that are always the same across your merchants. For example, if a particular questionnaire requirement always has the same answer for every merchant on your platform, you can pre-mark that response directly in the template PDF. Preczn will then overlay the merchant-specific and remaining questionnaire data on top of the template during signing.

If you need help preparing your SAQ template, contact Preczn support — we can work with your team to set up a template that fits your platform's needs.

⚠️

One template per environment

Each platform has a single SAQ template for test mode and a separate one for live mode. Uploading a new template replaces the previous one. The template must be in place before any signing operations will succeed.

Creating and Signing an SAQ

This is the primary workflow for platforms that build an SAQ completion experience into their application.

Step 1: Create a Draft SAQ

POST /v1/saq
{
  "merchantId": "mrch_8a3b2c1d",
  "type": "A"
}

This creates a new SAQ in draft state. You can optionally include pdfDetails with the initial request, or update them separately in the next step.

Step 2: Update the SAQ with Merchant Data

PATCH /v1/saq/{saqId}

Populate the draft with merchant information and questionnaire responses:

{
  "pdfDetails": {
    "thirdPartyProcessors": [
      {
        "name": "Stripe",
        "description": "Payment processing"
      }
    ],
    "section2Questionnaire": {
      "requirementResponses": {
        "3.1.1": "inPlace",
        "3.2.1": "inPlace",
        "8.2.1": "notApplicable",
        "9.4.1": "inPlace"
      }
    },
    "appendixC": [
      {
        "requirement": "8.2.1",
        "reason": "Not applicable - no direct access to cardholder data"
      }
    ]
  }
}

Updates are merged with existing data, so you can call this endpoint multiple times as the merchant progresses through your questionnaire UI.

Questionnaire response values:

ValueMeaning
inPlaceThe requirement is fully implemented
inPlaceWithCCWIn place with a compensating control worksheet
notApplicableThe requirement does not apply to this merchant
notInPlaceThe requirement is not implemented

How pdfDetails Maps to the PDF

When you generate a draft or signed PDF, Preczn takes the data you've provided in pdfDetails and populates your SAQ template by rendering text and marks directly onto the document:

  • Merchant and contact information — Business name, legal name, address, website, and contact details are rendered as text in the corresponding fields on the form.
  • Questionnaire responses — For each requirement response you provide, Preczn places an x in the matching checkbox column on the questionnaire pages. The response value determines which column is marked (In Place, In Place with CCW, Not Applicable, or Not In Place).
  • Third-party processors — Each processor name and description is listed as a line item on the processors page, spaced dynamically to accommodate your list.
  • Appendix C — Requirements marked as not applicable are listed with their explanations. Long reason text is automatically wrapped across lines (up to 16 items).

The result is a fully populated SAQ form that combines your platform's template (which may include pre-filled responses for requirements that are always the same) with the merchant-specific data you provide through the API.

📘

Draft SAQs only

Updates are only allowed while the SAQ is in draft state. Once signed, the SAQ is immutable.

Step 3: Preview the Draft PDF (Optional)

GET /v1/saq/{saqId}/file/draft

Returns a PDF generated on-the-fly from the current draft data. Use this to show merchants a preview of their SAQ before signing. The draft PDF reflects all pdfDetails provided so far but does not include signature fields.

The response is a binary PDF stream with Content-Type: application/octet-stream and a Content-Disposition: attachment header containing the filename. Ensure your HTTP client is configured to handle binary responses — the PDF is not base64 encoded.

Step 4: Sign the SAQ

POST /v1/saq/{saqId}/sign
{
  "signedOn": "2026-03-12T14:30:00Z",
  "signedBy": "Jane Smith",
  "signerTitle": "Director of Operations",
  "signersIP": "203.0.113.42"
}

This transitions the SAQ from draft to signed:

  1. Preczn populates your SAQ template with the merchant data and questionnaire responses
  2. Signature details are embedded into the PDF
  3. The completed PDF is stored securely
  4. The SAQ expiration is set to 365 days from signedOn
  5. The merchant record is updated with the latest signed SAQ and expiration date

After signing, the SAQ is immutable. A MERCHANT_UPDATED webhook event is published.

Step 5: Download the Signed PDF

GET /v1/saq/{saqId}/file/signed

Returns the final signed SAQ document. Like the draft endpoint, the response is a binary PDF stream (Content-Type: application/octet-stream) with a Content-Disposition: attachment header. Use this for archival, audit, or displaying the completed document to the merchant.

Uploading an Externally-Signed SAQ

If the merchant completes and signs their SAQ outside of your platform, you can upload the signed PDF directly.

POST /v1/saq/upload/{saqId}?signedOn=2026-03-12T14:30:00Z
Content-Type: multipart/form-data

Upload the signed PDF file (max 15MB) with the signedOn date as a query parameter.

This transitions the SAQ from draft to signed with creationMethod set to uploaded. The same merchant record updates and webhook events apply.

📘

Create the draft first

You must create a draft SAQ (POST /v1/saq) before uploading. The upload endpoint transitions an existing draft to signed — it does not create a new SAQ record.

Transmitting SAQs to Processors

After signing, you can transmit the SAQ to a supported payment processor.

POST /v1/saq/{saqId}/connection/{connectionId}/send

This sends the signed SAQ PDF and compliance metadata to the processor. Currently supported:

ProcessorWhat's Sent
PayrixSAQ PDF uploaded as a note document, merchant record updated with SAQ type and date
⚠️

Signed SAQs only

Draft SAQs cannot be transmitted. The SAQ must be in signed state, and the merchant must have an active connection with a processor MID configured for the specified connectionId.

The response includes a saqTransmissionHistory array tracking when and where the SAQ was sent.

Listing Merchant SAQs

GET /v1/saq/merchant/{merchantId}

Returns a paginated list of all SAQs for a merchant, ordered by creation date. Use cursor-based pagination with the lastKey parameter:

const allSaqs = [];
let lastKey = null;
let hasMore = true;

while (hasMore) {
  const params = new URLSearchParams({ limit: "25" });
  if (lastKey) params.set("lastKey", lastKey);

  const response = await fetch(
    `https://api.preczn.com/v1/saq/merchant/mrch_8a3b2c1d?${params}`,
    { headers: { "x-api-key": "your_api_key" } }
  );
  const result = await response.json();

  allSaqs.push(...result.data);
  hasMore = result.hasMore;
  lastKey = result.lastKey;
}

Monitoring Merchant Compliance

Each merchant record includes an saq object with lastSignedSAQ (the ID of the most recent signed SAQ) and lastSignedSAQExpiresOn (its expiration date). These fields are returned on both the Get Merchant and List Merchants endpoints, giving you the raw data to assess compliance status in your own application.

The List Merchants endpoint also supports SAQ-specific query parameters that let you filter merchants based on their compliance status. Use these to build compliance dashboards, trigger renewal reminders, or generate audit reports.

Query Parameters

ParameterTypeDescription
saqExpiredbooleanWhen true, returns merchants whose SAQ has expired or who have no SAQ on file. When false, returns merchants with a valid (non-expired) SAQ.
saqExpiringWithinDaysnumberReturns merchants whose SAQ expires within the specified number of days. Must be greater than 0.

These parameters can be used independently or combined with each other and any other List Merchants query parameters.

Examples

Find all merchants with expired or missing SAQs:

GET /v1/merchants?saqExpired=true

Find merchants whose SAQ expires within the next 30 days:

GET /v1/merchants?saqExpiringWithinDays=30

Find merchants expiring within 90 days, including those already expired:

GET /v1/merchants?saqExpiringWithinDays=90&saqExpired=true

This returns merchants in three groups:

  • SAQ expires within the next 90 days (expiring soon)
  • SAQ has already expired
  • No SAQ on file
📘

Combine with other filters

These parameters work alongside existing List Merchants filters. For example, you can combine them with status or label filters to narrow results to a specific segment of your merchant portfolio.

Test Mode

All SAQ endpoints respect the test/live mode of your API key. SAQs created in test mode are isolated from live data. Use test mode for development and integration testing, then switch to live mode API keys for production.