(c)2026, SIGNIX, Inc.

# SIGNiX Flex API — Knowledge for AI Coding Agents

This document is a **standalone knowledge file** for AI coding tools helping integrators build against the SIGNiX **Flex API**. It is **not** tied to any particular demo application or repository.

> **Official Flex API documentation**  
> [https://www.signix.com/apidocumentation](https://www.signix.com/apidocumentation)  
> Use that site (and linked pages) for exact request/response schemas, status codes, authentication service types, and client preferences. This file captures integration heuristics and field-proven patterns; when they conflict with the published schema, **follow the official docs** unless a note below explicitly records live tenant behavior that diverged and still worked.

> **Push Notifications documentation**  
> [https://www.signix.com/pndocumentation](https://www.signix.com/pndocumentation)  
> Webhook subscription (account-level Base URL and/or per-transaction client preferences), query parameters (`action`, `id`, `extid`, …), response requirements, retries, and TLS. Prefer push over polling for live status sync.

> **SIGNiX FlexAPI Postman collection**  
> Published docs and runnable samples: [https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ)  
> Covers the full lifecycle on Webtest (`SubmitDocument`, submitter/signer `GetAccessLink`, `DownloadDocument`, `ConfirmDownload`, plus AJAX cancel/suspend/resume and signer email/mobile/resend). Requests use form fields `method` + `request` (XML) and collection/environment variables for credentials—useful for humans and AI agents inspecting real XML shapes.

## How AI agents should use this file

1. Attach or paste this markdown into the coding agent’s context when implementing SIGNiX.
2. Implement the canonical sequence: **transport → SubmitDocument → (GetAccessLink and/or email) → push notifications → DownloadDocument → ConfirmDownload**. Prefer **push** for live status; use **QueryTransactionStatus** only as a fallback or reconciliation aid (see Push notifications).
3. Prefer documented Flex elements. **Do not invent XML tags** (see anti-patterns below).
4. When unsure of XML envelopes or AJAX control methods, cross-check the plain-text [Flex request XML samples](./FLEX-REQUEST-XML-SAMPLES.md) (and the [Postman FlexAPI collection](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ) when a browser can open it). For Form field/signature child order, use the [tagging cheat sheet](./FORM-TAGGING-ELEMENTS-CHEAT-SHEET.md).
5. Keep stack choices free: patterns are language-agnostic. A short **Python/Django illustration** appears at the end as one optional implementation style—not a requirement.
6. **Do not load the entire Flex API documentation page into context**—it is a large single page. Use the [agent reading list](#agent-reading-list-load-only-what-you-need) below (local companions + deep links) until per-method official pages exist.

### Authority order

1. Official Flex API / Push Notifications docs on signix.com (prefer **deep links / the section you need**, not the whole page)  
2. Verified notes in this knowledge file (especially “field-proven” callouts)  
3. Local companions: [FLEX-REQUEST-XML-SAMPLES.md](./FLEX-REQUEST-XML-SAMPLES.md), [FORM-TAGGING-ELEMENTS-CHEAT-SHEET.md](./FORM-TAGGING-ELEMENTS-CHEAT-SHEET.md)  
4. [SIGNiX FlexAPI Postman collection](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ) (sample requests and workflows; browser-friendly)  
5. Stack-specific examples (Django illustration, your project code)

### Agent reading list (load only what you need)

The main [Flex API documentation](https://www.signix.com/apidocumentation) page is very large for AI context windows. For each task, load **this knowledge file’s relevant section** (or skim via search) plus the **local sample/cheat sheet** and, if needed, only the **official deep link**—not the whole Flex page.

| Task | Load first (local) | Official deep link / page (authority) |
|------|--------------------|----------------------------------------|
| End-to-end / first slice | This file: Canonical sequence, Definition of done, Test plan | [Getting Started](https://www.signix.com/development-community-getting-started) |
| Transport / `method` + `request` | This file: Transport; [XML samples](./FLEX-REQUEST-XML-SAMPLES.md) intro | [Flex API](https://www.signix.com/apidocumentation) (HTTP Request / endpoints) |
| SubmitDocument envelope, CustInfo, Demo, DeliveryType | This file: SubmitDocument, CustInfo gotchas; [XML sample §1](./FLEX-REQUEST-XML-SAMPLES.md) | [SubmitDocument](https://www.signix.com/apidocumentation#SubmitDocument) |
| Form tagging — which approach | This file: Document tagging approaches | [SubmitDocument](https://www.signix.com/apidocumentation#SubmitDocument) |
| Form tagging — element names / child order | [Tagging cheat sheet](./FORM-TAGGING-ELEMENTS-CHEAT-SHEET.md) | [Text Tagging](https://www.signix.com/api-text-tagging) or [Coordinate tagging](https://www.signix.com/coordinate-based-tagging) |
| AcroForm field pitfalls | This file: PDF tagging pitfalls | Flex SubmitDocument + your PDF tooling docs |
| Notary / MeetingInfo | This file: Notary and MeetingInfo | [SubmitDocument additional options](https://www.signix.com/apidocumentation#SubmitDocumentAddnlOptions) |
| GetAccessLink (signer / submitter / embed) | This file: GetAccessLink; [XML samples §2–3](./FLEX-REQUEST-XML-SAMPLES.md) | [Flex API](https://www.signix.com/apidocumentation) (GetAccessLink sections) + Postman GAL samples |
| SubmitAjax (cancel / resume / email / QTS) | This file: SubmitAjax + QTS; [XML samples §6–7](./FLEX-REQUEST-XML-SAMPLES.md) | [AJAX API](https://www.signix.com/ajaxapi); QTS also on [Push docs](https://www.signix.com/pndocumentation) |
| Push webhooks | This file: Push notifications | [Push Notifications](https://www.signix.com/pndocumentation) |
| DownloadDocument / ConfirmDownload | This file: DownloadDocument and ConfirmDownload; [XML samples §4–5](./FLEX-REQUEST-XML-SAMPLES.md) | [Flex API](https://www.signix.com/apidocumentation) (DownloadDocument / ConfirmDownload) |
| Auth Service types | This file: MemberInfo field notes | [Flex API](https://www.signix.com/apidocumentation) (Authentication / Service Types) |
| Digital Billboard | Discovery / features notes in this file | [Digital Billboard](https://www.signix.com/apidocumentation#digitalbillboard) |

**Default prompt pack for a coding agent building the vertical slice:** this knowledge file + [FLEX-REQUEST-XML-SAMPLES.md](./FLEX-REQUEST-XML-SAMPLES.md) (§1, §4, §5) + [Push Notifications](https://www.signix.com/pndocumentation). Add the tagging cheat sheet only when placing fields.

---

## Key terms

- **Transaction** — The set of documents and parties sent in one **SubmitDocument** call. SIGNiX returns a **DocumentSetID**. The client may also send a **TransactionID** (client-chosen) for correlation.
- **SubmitDocument** — Creates and starts a transaction: documents, signers, authentication, field assignments.
- **DownloadDocument** — Retrieves completed signed PDFs and optionally the audit trail / certificate of completion. For signer file uploads submitted via Flex **`UserAttachments`**, include **`IncludeAttachmentData`** (placed **above** **`IncludeAuditData`** per vendor rules).
- **ConfirmDownload** — Confirms that your system has **successfully received and stored** the DownloadDocument results (signed PDFs, and audit trail / certificate when requested). Call it only after that persistence succeeds—not immediately after firing DownloadDocument. Required for retention policies such as **DelDocsAfter**.
- **GetAccessLink** — Returns a URL for the submitter (configuration/wizard) or a **signer** (signing experience). Often used to open the first signer in a **separate browser window** (avoids iframe / iOS cookie issues).
- **SubmitAjax** — Flex method that carries **AJAX transactional controls** (JSON inside `<Request>`) for an in-progress document set: cancel, suspend/resume, change signer email/mobile, resend pickup email, and related document edits. Official shapes: [SIGNiX AJAX API](https://www.signix.com/ajaxapi).
- **Push notifications** — Webhooks (GET with query parameters) for events such as `Send`, `partyComplete`, `complete`, `suspend`, `cancel`, `expire`. **Preferred** primary status path for new integrations. See [Push Notifications](https://www.signix.com/pndocumentation).
- **QueryTransactionStatus (QTS)** — Fallback status **poll** implemented as a **SubmitAjax** JSON `objectType` (not a top-level Flex `method=`). Prefer **push**; see Push notifications.
- **Client preferences (CPs)** — Per-transaction overrides via `<ClientPreference name="...">`. Includes push URL CPs and many signing/email options. See Flex docs → Client Preference Overrides.

---

## What is the SIGNiX Flex API?

The Flex API drives the SIGNiX digital signature platform (including notary features) over HTTPS using XML request/response bodies. Official reference: [Flex API documentation](https://www.signix.com/apidocumentation). For transaction status webhooks, see [Push Notifications](https://www.signix.com/pndocumentation).

**Capabilities include:**

- Creating transactions (signers, auth, documents, tags)
- Access links to embed or open SIGNiX UI (configuration and signing)
- Automatic orchestration (workflow, email invites)
- Push notifications / webhooks for transaction progress ([pndocumentation](https://www.signix.com/pndocumentation))
- Downloading signed documents and audit artifacts
- Configuring notary sessions
- Advanced options (Digital Billboard, Fraud Alert, white labeling, and more)

**Technical characteristics:**

- HTTPS POST with form-encoded `method` + `request` parameters
- Commands (RPC-style methods) and Notifications (push / webhooks)
- XML for request and response bodies
- Credentials inside the request XML (`CustInfo`), not as a separate HTTP auth header

---

## Credentials and endpoints

Flex API access must be enabled by SIGNiX Support. You need:

- **Sponsor** id  
- **Client** id  
- **Workgroup**  
- **UserId**  
- **Password**

Sponsor and Client are often the same for small setups. Workgroup is case-sensitive and must match the assigned value exactly.

Most Flex B2B samples and this knowledge file assume **CustInfo** credentials in the request XML. If your integration needs **OAuth** credentials instead (or in addition), contact SIGNiX—those can be provided when required.

**Endpoints:**

| Environment | URL |
|-------------|-----|
| **Webtest** (development) | `https://webtest.signix.biz/sdd/b2b/sdddc_ns.jsp` |
| **Production** | `https://signix.net/sdd/b2b/sdddc_ns.jsp` |

Start all development in **Webtest**. Switch to Production only for real transactions.

### CustInfo and Data gotchas

Agents often fail early SubmitDocument calls on credential / envelope details rather than tagging. Watch these:

**CustInfo**

- **Element names** — Use Flex names exactly: **`Sponsor`**, **`Client`**, **`UserId`**, **`Pswd`** (not `Password` in typical B2B samples), **`Workgroup`**.
- **Do not invent CustInfo children** — Stick to documented elements; do not copy non-Flex fields from other e-sign products. **Do not reuse the SubmitDocument CustInfo block verbatim on every method**—schemas differ (see table below).
- **Workgroup** — Exact match to the value SIGNiX assigned (**case-sensitive**). A one-character typo often returns “workgroup does not exist.”
- **Trim credentials** — Strip leading/trailing whitespace from Sponsor, Client, UserId, Pswd, and Workgroup when loading from env/config/UI. Invisible spaces cause opaque auth failures.

#### CustInfo fields by method

| Field | SubmitDocument | DownloadDocument | ConfirmDownload | GetAccessLink | SubmitAjax | Notes |
|-------|----------------|------------------|-----------------|---------------|------------|-------|
| **Sponsor**, **Client**, **UserId**, **Pswd** | Required | Required | Required | Required | Required | Core credentials on all of these |
| **Workgroup** | Required | Required (typical) | Required (typical) | Required (typical) | Required (typical) | Same assigned value; omit only if a method’s schema explicitly excludes it (rare—e.g. some CertifyDocument samples) |
| **Demo** | **Required** | **Do not send** | **Do not send** | **Do not send** | **Do not send** | **SubmitDocument only.** Values **`yes`** / **`no`** (not `true`/`false`). Align `yes` with Webtest and `no` with Production as your config intends. |
| **DelDocsAfter**, **EmailContent** | Optional / tenant-specific on SubmitDocument | **Do not send** | **Do not send** | **Do not send** | **Do not send** | SubmitDocument CustInfo extras when your account uses them |

**`Demo` is not optional on SubmitDocument.** Omitting it often yields a vague Flex error such as **`Required CustInfo parameter not specified`** **without naming the field**. If you see that on SubmitDocument, verify **`Demo`** is present (`yes` or `no`) before chasing other tags.

**Do not** copy `<Demo>` onto DownloadDocument (or GAL / ConfirmDownload / SubmitAjax). Including it there is a schema validation error (e.g. **`Invalid content was found starting with element 'Demo'`**).

**Request root / Data**

- **`xmlns`** — Root request elements (e.g. `<SubmitDocumentRq>`) use **`xmlns="urn:com:signix:schema:sdddc-1-1"`** in current Flex B2B samples. Wrong or missing namespace breaks schema validation.
- **`DocSetDescription`** — ASCII only for separators: use hyphen **`-`**, not Unicode en/em dashes. Some tenants also reject other decorative punctuation in titles—keep labels simple (e.g. `Account Opening - Sam Signer - Checking`).
- **Signer emails on Webtest** — Use **real, reachable** mailbox addresses for parties you intend to invite. Disposable/fake domains often block pickup email and make “email invite” testing look like an API failure.
- **`SuspendOnStart`** — **`yes`** / **`no`** strings (see Field notes), not `true`/`false`.
- **Schema order** — CustInfo then Data; inside Data, keep MemberInfo / Form / ClientPreference children in documented order (see Request structure). Wrong order is a frequent silent-looking validation error.

**Security:** See [Security for Flex integrations](#security-for-flex-integrations-and-ai-agents).

---

## Transport (how to send requests)

All B2B methods use the **same endpoint URL** and transport. The **`method`** parameter selects the call.

- **HTTP:** `POST` with body MIME type **`application/x-www-form-urlencoded`**
- **Form parameters** (case-sensitive; **lowercase** names):
  - **`method`** — Outermost request XML element name with `Rq` removed. Example: root `<SubmitDocumentRq>` → `method=SubmitDocument`. Confirm other method names in the Flex docs (e.g. GetAccessLink / GetAccessLinkSigner variants).
  - **`request`** — The full request XML string.
- **Response:** HTTP 200 with **Content-Type `text/xml`**. Follow the **Flex response parsing contract** below—do not treat HTTP 200 alone as business success.

Use any HTTP client your stack provides (fetch, axios, `requests`, `HttpClient`, etc.).

Runnable plain-XML samples (one per common method) are in [FLEX-REQUEST-XML-SAMPLES.md](./FLEX-REQUEST-XML-SAMPLES.md). The [SIGNiX FlexAPI Postman collection](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ) is useful in a browser; some coding agents cannot read it—prefer the samples file in that case.

### Flex response parsing contract

Apply this to **every** Flex B2B method (`SubmitDocument`, `GetAccessLink`, `DownloadDocument`, `ConfirmDownload`, `SubmitAjax`, etc.). Agents that skip these steps produce false “success” paths.

1. **Transport layer first** — Treat network failures, timeouts, HTTP **4xx/5xx**, empty bodies, and non-XML bodies as **errors**. Do not read business fields from a failed transport response.
2. **Parse with an XML library** — Build a DOM/tree from the body. Do **not** extract `StatusCode`, `DocumentSetID`, or URLs with regex or string splits (namespaces and formatting vary).
3. **Check `StatusCode` before anything else** — Locate **`StatusCode`** (commonly under `<Status>`). **`0`** = business success. Any other value (or missing/unparseable code) = failure. Surface **`StatusDesc`** (or equivalent) in logs/UI; do not continue as if the call succeeded.
4. **Only then read method-specific data** — On success, read the fields that method returns (examples below). On failure, treat payload data as unreliable even if some elements are present.
5. **Persist identifiers your later steps need** — Especially **`DocumentSetID`** after SubmitDocument (push `id`, DownloadDocument, ConfirmDownload, SubmitAjax, GetAccessLink all key off it). Keep your client **`TransactionID`** too (push `extid`).
6. **SubmitAjax is two-level** — Outer Flex `StatusCode` **and** each inner `<Response responseCode="…">` must succeed (see [SubmitAjax](#submitajax-ajax-transactional-controls)).

**HTTP 200 ≠ success.** A valid XML error envelope often arrives with HTTP 200 and a non-zero `StatusCode`.

#### What to read after StatusCode 0 (by method)

| Method | Persist / use (typical) |
|--------|-------------------------|
| **SubmitDocument** | **`DocumentSetID`** (required). Optional first-party pickup / signing URL if present; otherwise call GetAccessLink for party **1**. Optional meeting / notary links when those features are used. |
| **GetAccessLink** (signer or submitter) | The returned access **URL**; open or embed it per your UX rules. |
| **DownloadDocument** | Signed document binaries (often `<Form>` / related nodes), optional audit / certificate / attachments; map by order and/or RefID; **store before** ConfirmDownload. |
| **ConfirmDownload** | Confirm outer success only after your storage step already succeeded. |
| **SubmitAjax** | Outer success **plus** inner `responseCode` 0 for each Request. (Includes QTS when you poll via `objectType` `QueryTransactionStatus`—parse the JSON in the AJAX response, not a separate Flex method.) |

Exact element names can vary by method and schema version—confirm in the [Flex API docs](https://www.signix.com/apidocumentation) and the [Postman collection](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ). Prefer parsers that match on **local** tag names (ignore XML namespace prefixes) when your stack makes that easy.

#### Failure handling expectations

- Log **StatusCode**, **StatusDesc**, and a truncated response body (never log passwords from CustInfo).
- Do **not** invent a DocumentSetID, invent a signing URL, or mark the local transaction “sent” / “complete” when StatusCode ≠ 0.
- Retry only when appropriate (transient transport errors); a business StatusCode error usually needs a fixed request, not a blind retry.

---

## Canonical integration sequence

```mermaid
flowchart LR
  build[Build package]
  submit[SubmitDocument]
  access[Email and or GetAccessLink]
  events[Push notifications]
  download[DownloadDocument]
  confirm[ConfirmDownload]

  build --> submit --> access --> events --> download --> confirm
```

1. **Build and submit** — Assemble parties, PDFs, and tag assignments; call **SubmitDocument**. Store returned **DocumentSetID** (and your **TransactionID**). Ensure push can reach your app via **account-level** Base URL and/or optional **per-transaction** notify ClientPreferences.
2. **Signer access** — SIGNiX emails the first signer, and/or your app calls **GetAccessLink** to open/embed signing.
3. **Progress** — Drive status and completion from **push notifications**. Do **not** design the primary integration around polling **QueryTransactionStatus**.
4. **Complete** — On push `action=complete`, call **DownloadDocument**, **persist** signed documents (and audit/certificate if requested), and only then call **ConfirmDownload**.

```mermaid
flowchart TB
  R[SubmitDocumentRq]
  R --> CustInfo[CustInfo]
  R --> Data[Data]
  Data --> MemberInfo["MemberInfo 1..N"]
  Data --> Form["Form 1..N"]
  MemberInfo --> M1["Names email Service MobileNumber when SMS"]
  Form --> F1["RefID tags Length Data base64 PDF"]
```

### Minimal vertical slice (build this first)

For a first Webtest integration, implement **one** end-to-end path before adding dashboards, notary, AJAX controls, or multiple tagging styles:

1. Configure Webtest CustInfo credentials and a reachable **HTTPS push URL** (usually **account-level** Base URL; optional per-transaction ClientPreferences only if you need to override).
2. **SubmitDocument** with one PDF, one signer (`SelectOneClick` is fine), `SuspendOnStart` = `no`, placeholder SSN/DOB when unused, and Form tagging that matches your PDF approach.
3. On StatusCode **0**, persist **DocumentSetID** and your **TransactionID**.
4. Complete signing (email invite or GetAccessLink).
5. Push endpoint returns body **`OK`**; on `action=complete`, asynchronously **DownloadDocument** → store artifacts → **ConfirmDownload**.

Do **not** stop at “SubmitDocument returned HTTP 200.” That is not a finished integration.

### Definition of done (acceptance checklist)

Treat the slice as done only when all of the following are true:

- [ ] SubmitDocument response XML has **StatusCode 0** and a **DocumentSetID** stored on your transaction record  
- [ ] Push URL is configured; handler responds with exactly **`OK`** within a short time  
- [ ] At least one push event (`Send` and/or `partyComplete` / `complete`) updates local status idempotently  
- [ ] On `complete`, signed PDF(s) are **stored** in your repository (not only downloaded into memory)  
- [ ] **ConfirmDownload** runs only after that storage succeeds  
- [ ] Failure paths are handled: non-zero StatusCode, push for unknown id, download/storage failure (no premature ConfirmDownload)

Optional for a richer first slice (still after the above): GetAccessLink for signer 1; audit trail / certificate on DownloadDocument; a simple status page showing DocumentSetID and workflow state.

Automated coverage that backs this checklist: [Test plan for coding agents](#test-plan-for-coding-agents).

---

## SubmitDocument

### Response essentials

Follow the [Flex response parsing contract](#flex-response-parsing-contract). In short:

- Check **`StatusCode`** before using other elements; HTTP 200 alone is not success.
- On success, store **`DocumentSetID`** — push notifications send this value as query param **`id`**.
- The response may include a first-party pickup link. If absent, call **GetAccessLink** for party ordinal **1**.

### Request structure (conceptual)

Inside `<Data>`, a working order used in production integrations is:

1. TransactionID, DocSetDescription, optional FileName  
2. SubmitterEmail, SubmitterName, ContactInfo, DeliveryType  
3. **SuspendOnStart**  
4. Optional push **ClientPreference** elements **only when overriding** account-level notify (see [Per-transaction push URL](#per-transaction-push-url-client-preferences--optional))  
5. Optional **`NotificationSchedule`** ClientPreference (reminders + **transaction expiry**—see Field notes)  
6. **MemberInfo** blocks (signing order)  
7. Optional MeetingInfo (remote notary meeting)  
8. **Form** blocks (documents)

Exact required/optional children depend on your tenant and Flex schema—confirm in the official docs.

### Field notes (integration experience)

- **TransactionID** — Client-chosen; keep **unique** (timestamp or UUID). Used for idempotency and as push **`extid`**.
- **DocSetDescription** — Human-readable label (dashboards / email subjects). Use an **ASCII hyphen** (`-`) between parts; avoid Unicode en dash or em dash.
- **Submitter vs first signer** — SubmitterEmail / SubmitterName identify who initiated the package. The first **MemberInfo** is the first signer in workflow order. They may be different people.
- **DeliveryType** — **Required** on SubmitDocument (per Flex documentation). For standard secure document delivery / e-sign packages, send **`SDDDC`**. Do not omit it, treat it as legacy/optional, or invent other values unless SIGNiX has assigned a different enum for your use case.
- **SuspendOnStart** — Use **`yes`** / **`no`** (not `true`/`false`). **`no`** = send immediately and notify signers; **`yes`** = hold the transaction so the submitter can edit (e.g. tag in SIGNiX) before invites.
- **NotificationSchedule** — Optional **ClientPreference** that sets signer **reminder** times and the **transaction expiry**. Place it with other ClientPreferences (after SuspendOnStart / push CPs, before MemberInfo). Value shape: curly-brace comma-separated list immediately followed by units, e.g. `{10,60,180}minutes` or `{3,5,7,10}days` (hours also allowed). **Earlier numbers are reminders; the last number is always the expiry.** A single number means no reminders—only expiry at that time. If omitted, the **account / SIGNiX default** schedule applies (commonly on the order of `{3,5,7,10}days`—confirm for your tenant). See Flex docs → Client Preference Overrides / SubmitDocument.
- **MemberInfo — element order** — Schema order matters. Typical sequence: RefID, SSN, DOB, FirstName, MiddleName, LastName, Email, **Service**, then **MobileNumber** when using SMS auth, then optional elements (KBA, Notary, Digital Billboard, etc.).
- **SMS authentication** — Set **`Service`** to the appropriate SMS type (e.g. `SMSOneClick` / `SMSNoPassword` per the Flex docs) and include **`MobileNumber`**.
- **MobileNumber** — Required for SMS / SharedSecret-style services. Omit spaces; hyphens are often allowed—confirm for your service type.
- **SSN / DOB** — Needed for some auth methods (e.g. KBA). When the chosen **Service** does **not** require real identity data, new integrations should always send the documented placeholders: **`SSN` = `910000000`** (nine digits, **no** dashes or hyphens such as `910-00-0000`) and **`DOB` = `00/00/0000`**. Use real SSN/DOB only when the selected authentication service requires them. **DOB** format is **`MM/DD/YYYY`**.
- **Workgroup** — Must match the assigned value exactly (case-sensitive). See also [CustInfo and Data gotchas](#custinfo-and-data-gotchas).

### Form element (documents)

Each `<Form>` carries one PDF (base64) and tagging instructions. Child **element order** must follow the Flex schema. Do **not** use legacy names such as FormID, Title, ContentType, LengthOfData.

**Document format:** At this time SIGNiX Flex SubmitDocument supports **PDF only**. Convert Word/other sources to PDF **before** submit. Wherever the Form schema requires a MIME type, send exactly:

```xml
<MimeType>application/pdf</MimeType>
```

Do not invent other MIME values (`application/msword`, `image/png`, etc.) for Form document content—even if your upstream file was Word or an image; the bytes in `<Data>` must be a PDF and MimeType must match.

How you tell SIGNiX *where* to place signatures and data fields is a separate design choice—see **Document tagging approaches** below.

#### Length vs Data (critical)

Near the end of each Form (after tagging children), Flex expects:

- **`<Data>`** — The PDF as a **Base64** string (ASCII alphabet only: `A–Z`, `a–z`, `0–9`, `+`, `/`, `=`).
- **`<Length>`** — The size of the **decoded PDF bytes** (the file length **before** Base64), as a decimal integer.

**Wrong:** `Length = len(base64String)` (character count of the encoded payload).  
**Right:** `Length = len(pdfBytes)` (byte length of the raw PDF you encoded).

Field-proven failure mode: SIGNiX rejects the Form when Length is the Base64 string length. That value is often about **4/3** of the real PDF size (e.g. PDF `187458` bytes → Base64 string length `249944`). Agents commonly make this mistake when they encode first and then take `length` from the string they just produced.

When Length is wrong, the Flex failure response (**StatusDesc** / error text) typically states the length SIGNiX **expected** (the decoded PDF byte count). Use that number to confirm you should set `<Length>` from raw PDF bytes, not from the Base64 string.

**Safe pattern:**

1. Read the PDF as raw bytes.  
2. Set `length = byteLength(pdfBytes)`.  
3. Set `data = base64Encode(pdfBytes)`.  
4. Emit `<Length>{length}</Length><Data>{data}</Data>` (order per schema—Length before Data in common SubmitDocument samples).

The same Length rule applies anywhere Flex uses Form-style Length/Data for a PDF (including related methods that upload or certify a document). Do not use legacy **`LengthOfData`**.

---

## Document tagging approaches

Flex SubmitDocument supports three common ways to associate signing and data fields with a PDF. Pick based on how documents are produced and how much PDF control the integrating system has—not on which feels newest.

| Approach | Flex Form children (conceptually) | Best when |
|----------|-----------------------------------|-----------|
| **AcroForm** | `SignatureLine` + `SignField` / `DateSignedField` | Existing fillable PDF forms; you can generate or maintain PDFs with named form fields |
| **Text tags** | `TextTagField` / `TextTagSignature` (anchor text + offsets) | Dynamic documents; migrating from other e-signature products that already use text anchors; limited ability to manipulate PDF structure |
| **Fixed positioning** | `FixedPositioningField` / `FixedPositioningSignature` (X/Y/Width/Height) | Static layouts or runtime-generated PDFs where you already know coordinates |

All three still wrap the same outer Form envelope: RefID, Desc, FileName, **`MimeType` = `application/pdf`**, … tagging elements …, Length, Data (base64 PDF). Confirm exact element order and names in the [Flex API — SubmitDocument](https://www.signix.com/apidocumentation#SubmitDocument) docs, [Text Tagging](https://www.signix.com/api-text-tagging), and [Coordinate-based tagging](https://www.signix.com/coordinate-based-tagging).

**Element names and child order (field vs signature):** keep this knowledge file for *when* to choose an approach; use the companion **[Form tagging elements cheat sheet](./FORM-TAGGING-ELEMENTS-CHEAT-SHEET.md)** for *which XML children belong on the field vs the signature* (e.g. **`Type`** not `FieldType`; **`MemberInfoNumber`** only on signature/line; date links via **`TagName`** → **`DateSignedTagName`**).

### AcroForm (named PDF form fields)

**What it is:** The PDF already contains AcroForm widgets (signature and often date fields). SubmitDocument references those names via **`SignatureLine`** → **`SignField`** (and optional **`DateSignedField`** / **`DateSignedFormat`**), with **`MemberInfoNumber`** assigning each line to a party.

**Choose AcroForm when:**

- You already ship **fillable PDF forms** (faculty / compliance / legacy form libraries).
- Your stack can **generate or maintain PDFs** with stable field names (ReportLab, Acrobat, vendor form tools, etc.).
- You want SIGNiX to bind to the same field names your other systems already use.

**Notes:** Field names in the PDF must match `SignField` / `DateSignedField` exactly. Prefer a single merged `/FT /Sig` widget per signature name (see **PDF tagging pitfalls** below).

### Text tags (anchor strings in the document)

**What it is:** The PDF (often produced from Word or other generators) contains **anchor text** that SIGNiX searches for. SubmitDocument sends **`TextTagField`** / **`TextTagSignature`** with **`AnchorText`**, optional offset/size, and **`MemberInfoNumber`**. See [Text Tagging](https://www.signix.com/api-text-tagging).

**Choose text tags when:**

- Documents are **dynamic** (content and pagination vary by transaction).
- You are **migrating from another e-signature provider** that already uses text-tag / anchor conventions.
- The integrating system has **limited ability to manipulate PDFs** (cannot reliably inject AcroForm fields or compute page coordinates).
- Authors can place markers in Word/templates (`%signature1`, date anchors, etc.) without a PDF engineering pipeline.

**Notes:** Prefer **unique** anchor text per field within a document. If unique visible text is impossible, revise the template or use **white-on-white** (“white text”) anchors so SIGNiX can find them without showing markers to signers. Emit date-related text tags before signature tags when the schema expects that order.

### Fixed positioning (explicit coordinates)

**What it is:** You supply page geometry in SubmitDocument—typically **`FixedPositioningField`** (e.g. DateSigned, NotarySeal, text/checkbox) and **`FixedPositioningSignature`** (signature / initial)—with **XPosition**, **YPosition**, **Width**, **Height**, page, and **MemberInfoNumber**. See [Coordinate-based tagging](https://www.signix.com/coordinate-based-tagging). Link related fields with tag names (e.g. **`DateSignedTagName`**, **`NotarySealTagName`**) when dates or seals must attach to a signature.

**Coordinate system (per SIGNiX docs — one answer):**

| Concept | Value |
|---------|--------|
| **Page origin `(0,0)`** | **Upper-left** corner of the page |
| **+X / +Y** | Right and **down** |
| **Units** | PDF points (**1/72 inch**). Letter page ≈ **612 × 792** |
| **`XPosition` / `YPosition`** | The **lower-left corner of the field box**. From that corner, **Width** and **Height** define the box |

Both “top-left” and “lower-left” appear in casual discussion because they name **different things**: the **page origin** is upper-left; the **field placement point** you send is the field’s **lower-left**. Do **not** treat X/Y as the top-left of the control. Do **not** confuse this with raw PDF user-space (often **bottom-left** origin, Y up)—convert if your layout engine uses that system.

**Choose fixed positioning when:**

- Documents are **static** (same layout every time), or
- You **generate PDFs at runtime** and already know fixed coordinates from your layout engine (drag-and-drop tagger, ReportLab page geometry, branded signoff pages, etc.).

**Notes:** Put non-signature **`FixedPositioningField`** blocks before **`FixedPositioningSignature`** (required by Flex samples). Associate date/seal tags to the correct signature via **`TagName`** / **`DateSignedTagName`** (and seal equivalents)—see the [tagging cheat sheet](./FORM-TAGGING-ELEMENTS-CHEAT-SHEET.md). Fields outside the page produce a verbose Flex error naming the Form/field. Do **not** invent `FieldType` or put **`MemberInfoNumber`** on the date/field block.

### How to decide (short)

1. **Already have named form fields in the PDF?** → AcroForm.  
2. **Have (or can add) text anchors, but not form fields or coordinates?** → Text tags.  
3. **You own layout and know exact box positions?** → Fixed positioning.

Many products support more than one approach across document types (static forms vs generated packets vs Word-derived PDFs). Use one method **per Form**; do not mix conflicting tagging styles for the same document unless Flex documentation explicitly allows it.

### PDF tagging pitfalls (AcroForm signatures)

When your app **generates** fillable PDFs for the AcroForm approach:

- Give each signature field a stable name that matches SubmitDocument **`SignField`**.
- Per the Flex API docs: **field names may not exceed 1024 characters, nor include special characters such as double quotes.**
- Prefer a **single merged** field+widget annotation: `/Type /Annot`, `/Subtype /Widget`, `/FT /Sig`, `/T (ExactName)` with **no** `/Kids` parent tree and **no** MuPDF “SIGN” appearance chrome.
- Some PDF libraries (and iText 7) expose a synthetic **`FieldName.1`** for a widget kid. That is usually the **same** annotation—not a second independent field. Renaming or targeting `FieldName.1` as a dotted hierarchy can **orphan** the widget and break signing. Target the real field name only.
- Date-signed AcroForm text fields should use the name you pass in **DateSignedField**.

---

## Notary and MeetingInfo (when required)

Skip this section for a first non-notarized vertical slice. Add eNotary only when the product needs remote online notarization (RON) or an in-workflow notary party. Official overview: [Flex API — SubmitDocument additional options / notarization](https://www.signix.com/apidocumentation#SubmitDocumentAddnlOptions).

### Notary party (`NotaryInfoAdded`)

- **At most one** eNotary per transaction, and that party **must be the last** MemberInfo (last in signing order / highest workflow order when WorkflowOrder is used).
- The notary **email must match a registered eNotary** in SIGNiX (with an eNotary profile). If no match, SubmitDocument fails and StatusDesc explains the mismatch—do not invent notary credentials.
- Notary **MemberInfo** is a **slim** block compared with regular signers. Field-proven / documented shape uses only the children SIGNiX lists for eNotary (commonly optional **`RefID`**, optional **`WorkflowOrder`**, required **`Email`**, required **`NotaryInfoAdded`**). Do **not** paste a full signer MemberInfo (SSN, Service, MobileNumber, etc.) onto the notary row unless the current Flex schema for your tenant explicitly allows it.
- **`NotaryInfoAdded`** — send **`true`** when this party is the eNotary (vendor docs use `true` here; this is **not** the same vocabulary as CustInfo **`Demo`** `yes`/`no`).

### Seal / stamp association on the Form

Notary signatures need a **seal/stamp field** associated with the notary’s signature tag:

- Emit a seal field in the Form tagging (e.g. **`Type` = `NotarySeal`** for text-tag or fixed-positioning fields, or an appropriately configured AcroForm multi-line stamp field per Flex notarization docs).
- On the notary’s signature element, set **`NotarySealTagName`** (or the schema’s equivalent) to the **name/tag** of that seal field so SIGNiX binds signature + seal.
- Place seal fields with enough size; undersized or missing seal geometry is a common submit/validation failure.
- For AcroForm stamp fields, follow Flex guidance (including multi-line flag requirements when documented)—do not assume a normal single-line text field is enough.

### Remote meeting (`MeetingInfo`)

For **scheduled remote** notarization meetings, include **`<MeetingInfo>`** in SubmitDocument **`<Data>`**:

- **Placement:** after the **last MemberInfo** (including the notary) and **before** the first **`<Form>`** (same order as the canonical request structure above).
- **Typical children (field-proven):** **`Date`**, **`Time`**, **`Timezone`**, **`Duration`** (Duration last). Confirm names/formats in the Flex additional-options docs—timezone values are an **allow-list**, not free text.
- **Scheduling vs transaction expiry:** The meeting **start** must be in the **future**, and it must **not fall outside** the transaction’s **expiry** window. That expiry comes from the **NotificationSchedule** ClientPreference on the transaction (last number in the schedule)—or from the account/SIGNiX **default** schedule when you omit the preference. Validate before submit so agents do not schedule a meeting after the package would already have expired (and do not send past start times).
- **Duration:** commonly **15 / 30 / 45 / 60** minutes when using that Flex option.

### Response fields to persist

On StatusCode **0**, parse and store when present:

- **`SubmitterMeetingLink`** — host / “start meeting as submitter” URL (open in a new tab; do **not** treat it as a signer pickup link).
- Optional echoed **`MeetingInfo`** (or related meeting ids) for your dashboard.

Keep meeting link storage on the same transaction row as DocumentSetID (see [Suggested data model](#suggested-data-model-integrator-app)).

### Agent pitfalls

- Adding notary + MeetingInfo before ordinary submit → push → download works  
- Putting MeetingInfo after Forms or before MemberInfo  
- Using a non-registered notary email  
- Forgetting **`NotarySealTagName`** (or seal field) so the notary signature has no stamp binding  
- Treating **`SubmitterMeetingLink`** as GetAccessLink signer URL  

---

## GetAccessLink (submitter vs signer)

Same Flex endpoint and transport as SubmitDocument (`method=GetAccessLink`, root typically `<GetAccessLinkRq>`). CustInfo + `<Data>` with **DocumentSetID**. The **role** you request decides whether you open the **submitter/wizard** experience or a **signer** pickup session—do not conflate them.

Confirm exact child names and allow-flags in the [Flex API docs](https://www.signix.com/apidocumentation) and the [Postman FlexAPI collection](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ) (submitter and signer samples).

### When to call which

| Goal | Role in request | Typical `TransactionRole` | When |
|------|-----------------|---------------------------|------|
| Place/edit tags in SIGNiX UI after a **suspended** submit | **Submitter** | Literal **`Submitter`** | `SuspendOnStart` = `yes`; open wizard, user Saves, then **SubmitAjax Resume** (and/or send) |
| Let a party sign (email deep-link missing, portal “Sign now”, or in-person facilitation) | **Signer** | Integer ordinal **`1`**, **`2`**, … matching SubmitDocument **MemberInfo** order | After submit (and after Resume if the set was suspended) |

**Not required for the minimal vertical slice** if SIGNiX emails signers and you only wait for push → download. Add GAL when you embed, open a window, or facilitate in-person signing.

**Do not** use submitter GAL to “sign as party 1,” or signer ordinal GAL to open the tagging wizard.

### Submitter GAL (wizard / configuration)

- Used when the package was submitted **suspended** so the submitter can tag or adjust before invites.
- Request shape (conceptual): DocumentSetID, **`TransactionRole` = `Submitter`**, plus vendor **Allow…** flags for field/task editing as documented for submitter access.
- After the user **Saves** in SIGNiX, your app typically calls **SubmitAjax `Transaction_Resume`** so the workflow can proceed (see [SubmitAjax](#submitajax-ajax-transactional-controls)).
- Remote delivery after resume: rely on email / normal pickup. In-person after resume: call **signer** GAL per party (next subsection).

### Signer GAL (party pickup)

- **`TransactionRole`** is the **1-based party ordinal** (first MemberInfo → `1`), not the MemberInfo **RefID** string and not AJAX **`P01`**.
- Always send **`Embedded`** and **`ContainerOrigin`** when your live schema requires them (common for modern GAL-signer samples).
- Optional: **`InpersonAuthentication`**, **`GetNormalPickupLink`**, **`FinishURL`**, **`CancelURL`**.

### Embedded / ContainerOrigin / InpersonAuthentication

| Scenario | `Embedded` (typical) | `ContainerOrigin` | `InpersonAuthentication` | Notes |
|----------|----------------------|-------------------|--------------------------|-------|
| Open signing URL in a **new window / tab** | `no` (or omit per schema) | Your app origin when required | usually omit / `no` | Prefer for **iOS** (third-party cookie limits in iframes) |
| Embed signing in an **iframe** (facilitated / portal) | `iframe` or **`sameframe`** per tenant samples | **Origin of the host page** that embeds the frame (scheme + host) | see rows below | Match Postman / Flex samples for your embed mode string |
| **Kiosk / device-present** in-person (force in-person auth UX) | embed mode for iframe | host origin | **`yes`** | SIGNiX in-person authentication path |
| **In-person facilitation** but keep **SubmitDocument MemberInfo** auth (SMS, SelectOneClick, etc.) | **`sameframe`** (or documented iframe mode) | host origin | **`no`** | Still embed; do **not** layer a second kiosk auth on top of compose-time Service |
| Remote email invite only | — | — | — | Often **no signer GAL** at all |

**FinishURL / CancelURL:** For iframe hosts, **omit** them unless you have validated framed navigation. SIGNiX may load those URLs **inside** the iframe; many apps send **`X-Frame-Options: deny`** on finish/cancel pages and the frame goes blank. Advance the host UI with your own control after the browser completion message (below).

**Embedding allow list (Webtest and Production):** SIGNiX signing/wizard pages are framed only for approved host origins. Clients who plan to embed GetAccessLink (submitter or signer) in an iframe should **contact SIGNiX** so their host URL(s) can be added to the **allow list** for the relevant environment (**Webtest** and/or **Production**). Without that, the browser may block the frame (e.g. X-Frame-Options / framing policy), which looks like a broken embed even when GAL returned a valid URL. Separate-window (non-iframe) pickup does not require this framing allow-list step.

**iOS:** Prefer a **separate window** for signer GAL unless you have proven embed settings for your tenant.

### Browser completion message (embedded signing)

When the signing experience is embedded (iframe) and your host page advances the workflow with a control such as **Finished signing**, do **not** rely only on the facilitator guessing that the signer is done. SIGNiX notifies the **parent window** via **`window.postMessage`** when that party’s session completes.

**Field-proven pattern:**

- Listen for `window` **`message`** events.
- Require **`event.origin`** to match the origin of the GetAccessLink signing URL (so unrelated windows cannot unlock your UI).
- Treat the payload as complete when **`event.data`** (string, or a JSON-serialized object) contains **`transactionComplete`** in a **case- and whitespace-insensitive** check. Vendor strings such as **`Signix says: transactionComplete`** match this rule.
- Keep the host “advance / Finished signing” control **disabled** until that message arrives; then enable it so the facilitator can continue.

This browser signal is complementary to **push** `partyComplete` / `complete` events: use the postMessage for **immediate UX in the embedding page**; use **push** for **server-side** status and download orchestration.

---

## SubmitAjax (AJAX transactional controls)

Use **SubmitAjax** after SubmitDocument when you need to control an **in-progress** document set without sending the submitter through GetAccessLink-Submitter for every edit. Same Flex endpoint and transport (`method=SubmitAjax`, `request=` XML). Server-side only—credentials stay in CustInfo.

Official reference: [SIGNiX AJAX API / Transactional API Controls](https://www.signix.com/ajaxapi). Sample envelopes: [Postman FlexAPI collection](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ).

### Envelope

`<SubmitAjaxRq>` → CustInfo (same as other Flex methods) → `<Data>` with:

- **DocumentSetID** (from SubmitDocument), **or** TransactionID (36-char client id) per vendor docs  
- **`<Request>`** — JSON for one AJAX operation (XML-escape the JSON text so `&`, `<`, quotes survive inside XML)

Sessionless SubmitAjax auto-saves changes; you do not need a separate logout. Prefer the **single-request** form for lifecycle actions.

### Common `objectType` values

| Control | Typical `<Request>` JSON (conceptual) | When |
|---------|----------------------------------------|------|
| Cancel | `{'objectType':'Transaction_Cancel'}` | Void an in-flight set (not already complete / cancelled / expired) |
| Suspend | `{'objectType':'Transaction_Suspend'}` | Pause an in-flight set |
| Resume | `{'objectType':'Transaction_Resume'}` | Release a set that was suspended (`SuspendOnStart` = `yes`, or after Suspend) so signing can proceed |
| Change email / mobile | `Transaction_SaveDeltas` + one `Transaction_UpdateRole` | Correct contact data on an in-process party |
| Resend pickup email | `Transaction_SendPartyEmail` | After an email change, or to re-invite; does **not** run automatically when email is updated |
| Query status (QTS) | `{'objectType':'QueryTransactionStatus'}` (plus DocumentSetID on `<Data>` as for other SubmitAjax calls) | Fallback poll only—see [QueryTransactionStatus](#querytransactionstatus--fallback-only) |

Exact JSON for SaveDeltas / SendPartyEmail (including `P01` / `P02` role ids and email vs phone tables) is in the [AJAX API](https://www.signix.com/ajaxapi) examples—copy those shapes; do not invent keys.

### Field-proven rules

- **Single request for Suspend / Resume / Cancel** — Do **not** pack Suspend + edit + Resume into one multi-`<Request>` SubmitAjax. Vendor docs: multi-request batches are **not** guaranteed sequential; use separate SubmitAjax posts for ordered lifecycle steps.
- **Email change ≠ resend** — `Transaction_UpdateRole` updates the party email only. Follow with a **separate** `Transaction_SendPartyEmail` (`includeLink` true, `RoleId` = `P01` / `P02` / …, `emailAddress` = the **new** address) so pickup mail goes to the corrected mailbox.
- **One attribute per SaveDeltas** — Do **not** change email and mobile in the same `Transaction_SaveDeltas` call.
- **Party ids** — AJAX role ids are **`P01`, `P02`, …** in SubmitDocument MemberInfo order. That is often **different** from MemberInfo **RefID** strings used for tags/push (e.g. `Signer 1`). Map ordinal → `Pnn` for AJAX; keep your own RefID map for push `partyComplete`.
- **Response checking** — Outer Flex **StatusCode 0** only means the server ran the AJAX payload. Also inspect each `<Response responseCode="…">` (and its body). Non-zero outer or inner codes are failures.
- **When to add this** — Not required for the [minimal vertical slice](#minimal-vertical-slice-build-this-first). Add Cancel / email-mobile / Resume after submit → push → download → ConfirmDownload works.

Optional advanced AJAX (upload/remove/download a single document on a suspended set) is documented on the same AJAX page; leave it until basic controls work.

---

## Push notifications (preferred over QueryTransactionStatus)

**Strong preference for new integrations:** implement **push notifications** as the primary way to learn that a transaction was sent, a party completed, or the package finished. Wire your app so that **`action=complete`** (and related events) drive status updates and kick off DownloadDocument—not a polling loop.

**Why push first:**

- Near-real-time status without hammering the Flex endpoint  
- Clear event semantics (`Send`, `partyComplete`, `complete`, …) already mapped to dashboard/state machines  
- Same DocumentSetID / TransactionID correlation you store at submit time  
- Avoids missing completion windows if polling intervals are coarse  

**Push URL can be configured at the account level** (Base URL set with your SIGNiX representative). Many integrations use **only** that account-level URL and never send push ClientPreferences on SubmitDocument.

### Per-transaction push URL (client preferences) — optional

Emit **`UseClientNotifyVersion2`** / **`TransactionClientNotifyURL`** **only if** you intentionally want a **per-transaction** (or per-environment) notify URL—for example different callbacks for staging vs production apps, or overriding the account Base URL for specific packages. If the account-level Base URL already points at your handler, **omit** these ClientPreferences.

When you do use them, place both **inside SubmitDocument `<Data>` immediately after `<SuspendOnStart>` and before** any `NotificationSchedule` ClientPreference and the first `<MemberInfo>`:

```xml
<ClientPreference name="UseClientNotifyVersion2">yes</ClientPreference>
<ClientPreference name="TransactionClientNotifyURL">https://your-app.example.com/signix/push</ClientPreference>
```

Field-proven tip: a URL **without** a trailing slash often works even when the app serves `/signix/push/` (SIGNiX follows the redirect). Prefer HTTPS. If your public URL is derived behind a reverse proxy, ensure the app emits `https://` (proxy-aware absolute URL construction).

### Push callback shape

SIGNiX sends a **GET** to your URL with **query parameters only** (no body). See [Push Notifications](https://www.signix.com/pndocumentation).

| Parameter | Meaning |
|-----------|---------|
| **action** | Event type (case-sensitive): e.g. `Send`, `partyComplete`, `complete`, `suspend`, `cancel`, `expire` |
| **id** | DocumentSetID |
| **extid** | Your TransactionID (if enabled) |
| **pid** / **refid** | Party identifiers on party-related events (prefer **refid** when both present) |
| **ts** | Event time (`yyyy-MM-dd'T'HH:mm:ss`, often Eastern unless configured otherwise) |

**Response requirement:** HTTP **200** with body exactly **`OK`**. Always return that for bad/unknown payloads so SIGNiX does not retry endlessly. Respond quickly; run DownloadDocument and heavy work **asynchronously** after acknowledging.

**What your push handler should do (minimum):**

1. Parse `action`, `id`, and `extid` (and `refid`/`pid`/`ts` when present).  
2. Look up the local transaction by **`id`** (DocumentSetID) or **`extid`** (TransactionID).  
3. Update status / signer progress using the mapping below (**idempotent**).  
4. On **`complete`**, enqueue **DownloadDocument → store → ConfirmDownload** (do not block the HTTP response on that work).  
5. Return body **`OK`**.

**Status mapping (typical):**

| action | Suggested local status |
|--------|-------------------------|
| Send | Submitted / Sent |
| partyComplete | In progress (advance signer progress; de-dupe by refid/pid) |
| complete | Complete (terminal); start download pipeline |
| suspend | Suspended (terminal unless you model resume) |
| cancel | Cancelled (terminal) |
| expire | Expired (prefer distinct from Cancelled) |

Make updates **idempotent**. Treat complete / cancel / expire (and usually suspend) as terminal so out-of-order retries do not regress state. Prefer a dedicated **Expired** status rather than overloading Cancelled. SIGNiX may retry the same event; de-dupe by event identity or by “already terminal / already counted refid.”

### QueryTransactionStatus — fallback only

**QTS is not a top-level Flex method.** Calling `method=QueryTransactionStatus` fails with errors such as **`Requested method does not exist: QueryTransactionStatus`**. Status polling is a **SubmitAjax** single request whose `<Request>` JSON uses **`objectType`: `QueryTransactionStatus`** (DocumentSetID in `<Data>` like other SubmitAjax calls). The useful payload is the **JSON** inside the AJAX **`Response`** (not a separate Flex method schema).

It is **not** the recommended primary path for production status sync—**push is**. Documented under [Push Notifications — other options](https://www.signix.com/pndocumentation).

Use QTS only when:

- Reconciling after a missed or failed push (ops / repair job)  
- Local development where a public HTTPS push URL is temporarily unavailable  
- A human-triggered “refresh status” control in an admin UI  

**Rate limit:** Space polls **≥ 10 minutes** apart per transaction (longer is better). More frequent polling is misuse relative to push and can collide with live signer activity.

**Reading completion:** Treat the AJAX JSON as an opaque vendor status blob—look for a clear **Complete** / finished-workflow signal in the documented fields (wording and nesting vary; do not hard-code a single fragile path as the only source of truth). Prefer **push `action=complete`** to start DownloadDocument; use QTS only to reconcile when push was missed. Full request/response examples live on the push-docs page—copy from there rather than inventing a Flex `QueryTransactionStatusRq`.

**CustInfo:** Same core credentials as other SubmitAjax calls (**no `Demo`**). Follow the [CustInfo-by-method table](#custinfo-fields-by-method); do not assume a shortened “four-field” example on an older doc page means Workgroup is optional for your tenant.

Do **not**:

- Use `method=QueryTransactionStatus` (use **`method=SubmitAjax`** + QTS `objectType`)  
- Poll with an interval **shorter than 10 minutes** as a substitute for push  
- Trigger DownloadDocument solely from QTS on a schedule while ignoring push `complete`  
- Teach agents that push and QTS are equal defaults—**push is the default**; QTS is supplemental

---

## DownloadDocument and ConfirmDownload

When the transaction is **complete**:

1. **DownloadDocument** with CustInfo + DocumentSetID.  
2. **Parse and store** signed PDFs (and audit trail / certificate of completion when requested) in your document repository. Treat HTTP success alone as insufficient—verify you obtained the expected artifacts and that writes succeeded.  
3. **ConfirmDownload** only after that storage step succeeds (required when using DelDocsAfter).

**ConfirmDownload timing:** Do **not** send ConfirmDownload a moment after DownloadDocument “for completeness.” The call tells SIGNiX you have the materials; it should correlate with **actual successful storage** of the signed documents and related artifacts (audit trail, certificate, attachments as applicable). If download or storage fails, retry DownloadDocument / fix storage—do not ConfirmDownload yet. If you ConfirmDownload too early, SIGNiX may apply retention (e.g. DelDocsAfter) while your system still lacks durable copies.

**Field-proven request ordering inside `<Data>`:** place **`AuditDataFormat`** (e.g. `pdf`) **before** **`UseConfirmDownload`**. Using `AuditTrailFormat`, or putting `AuditDataFormat` after `UseConfirmDownload`, has caused schema validation errors on live tenants.

**Field-proven response shapes:** signed documents often appear as **`<Form>`** under response `<Data>`; audit PDF as **`<AuditReport>`**; certificate as **`<CertificateOfCompletion>`**. Prefer parsers that tolerate both documented and observed element names. Map returned docs by order and/or RefID to the Forms you submitted. If the same logical document can appear in multiple SIGNiX transactions, keep download bookkeeping **per DocumentSetID**, not “any Final version exists.”

---

## High-level architecture

Most integrations treat the business app as coordinator:

```mermaid
flowchart TB
  subgraph actors
    BU[BusinessUser]
    S[Signers]
    N[Notaries]
  end

  subgraph ia[IntegratedApplication]
    BL[BusinessLogicAndUI]
    D[Dashboard]
    E[EventStatusSync]
    TP[TransactionPackager]
    E --> D
    TP --> D
  end

  DG[DocumentGeneration]
  SX[SIGNiX]
  DB[(Database)]
  DR[(DocumentRepository)]

  BU --> ia
  E <--> SX
  TP --> SX
  ia --> DG
  ia --> DB
  ia --> DR
  SX --> S
  SX --> N
```

- **Transaction Packager** — Builds SubmitDocument (and AddDocument for suspended packages), including push ClientPreferences when using per-transaction notify URLs.
- **Event / status sync** — **Push listener** as the primary path; optional QTS for reconciliation. Triggers download on push `complete`.
- **Dashboard** — Surfaces status and signer progress inside the integrator UI.
- **Document generation / repository** — Produce PDFs and store signed artifacts.

### UI considerations

- Creation: choose documents, signers, auth; send now vs suspend for tagging.
- Signing: email link vs GetAccessLink; separate window vs iframe (mind iOS).
- Dashboard: map push actions to human-readable status and signer counts.

### Features often used only via API

- Embed / submitter wizard flows  
- Digital Billboard (post-sign CTA)  
- Text tagging for generated PDFs  
- Fraud Alert instant feedback  

---

## Suggested data model (integrator app)

Persist enough local state that push, download, GAL, and SubmitAjax never depend on guessing. Exact table names are yours; the **fields and indexes** matter.

### Transaction (document set) record

Minimum columns after a successful SubmitDocument:

| Field | Source / purpose |
|-------|------------------|
| **Client TransactionID** | Value you sent in SubmitDocument; unique; push query param **`extid`** |
| **DocumentSetID** | From SubmitDocument response; unique when present; push query param **`id`**; key for DownloadDocument, ConfirmDownload, GetAccessLink, SubmitAjax |
| **Local / workflow status** | Your enum driven primarily by push (`Send`, `partyComplete`, `complete`, `suspend`, `cancel`, `expire`)—see push status mapping |
| **Submitted / last-event timestamps** | Audit and idempotency |
| **Delivery mode** (optional) | e.g. remote vs in-person—drives whether you call signer GAL |
| **Suspend / resume flags** (optional) | If you use `SuspendOnStart` = `yes` |

**Indexes / lookups:** You must be able to resolve a push callback by **`id` → DocumentSetID** and/or **`extid` → TransactionID**. Prefer unique constraints on both identifiers once known.

Do **not** invent a DocumentSetID locally. Leave it null/empty until StatusCode **0** returns one.

### Parties (signers / members)

Store one row (or structured list) per SubmitDocument **MemberInfo**, in order:

| Field | Purpose |
|-------|---------|
| **Ordinal** (`1`, `2`, …) | GetAccessLink signer **`TransactionRole`**; display order |
| **RefID** | Value you sent on MemberInfo; match push **`refid`** on `partyComplete` |
| **AJAX role id** (derived) | `P01`, `P02`, … for SubmitAjax email/mobile/resend—**not** the same string as RefID |
| **Email / mobile / Service** | As submitted; update locally if you change them via SubmitAjax |
| **Completed?** | Set from `partyComplete` (idempotent by RefID/ordinal) |

### Documents / forms

| Field | Purpose |
|-------|---------|
| **Form RefID / order** | Map DownloadDocument returned files back to what you submitted |
| **Unsigned source** | Your generated/uploaded PDF reference |
| **Signed artifact storage key** | After download; bookkeeping **per DocumentSetID** (same logical doc may appear in multiple transactions) |
| **Audit trail / certificate flags or paths** | When requested on DownloadDocument |
| **ConfirmDownload done?** | Only set **true** after durable storage succeeded |

### Push / download bookkeeping (strongly recommended)

- **Last push action + timestamp** (and optionally a de-dupe key for retries)  
- **Download attempted / succeeded at**  
- **ConfirmDownload succeeded at**  
- Optional: raw truncated last error (never store CustInfo passwords)

### Mapping cheat sheet

| External signal | Look up local row by |
|-----------------|----------------------|
| Push `id` | DocumentSetID |
| Push `extid` | Client TransactionID |
| Push `refid` / party | Party RefID (fallback: ordinal if you control both) |
| GetAccessLink signer | DocumentSetID + party ordinal |
| SubmitAjax `P01`… | Party ordinal → `P{nn}` |
| DownloadDocument / ConfirmDownload | DocumentSetID |

Keep business entities (deal, application, job) as parents of this SIGNiX transaction record so dashboards can join product context to workflow status without parsing XML again.

---

## Discovery checklist (before coding)

**Transaction creation**

- Use cases, volumes, current vs desired flow  
- How documents are generated and tagged (**AcroForm** vs **text tags** vs **fixed positioning**)  
- Signer sources and authentication levels  
- Field-to-signer assignment  
- SuspendOnStart vs immediate send  
- Email invite vs embedded / GetAccessLink  

**Execution**

- Dashboard and progress tracking  
- **Push notifications** as the primary status path (HTTPS callback URL); QTS only for fallback/reconciliation  

**Completion**

- Where signed PDFs, audit trail, and certificates are stored  
- ConfirmDownload / retention (DelDocsAfter)  
- Download pipeline triggered from push `complete` (async after `OK`)  

**Environment**

- Webtest vs Production credentials  
- Branding (emails, Digital Billboard)  
- Public HTTPS endpoint for push (required for a complete integration)  
- Where credentials live (env/secret store—not source control); see Security below  

---

## Security for Flex integrations (and AI agents)

Flex B2B requests carry **live credentials inside CustInfo**. Treat every SubmitDocument / GetAccessLink / DownloadDocument / SubmitAjax payload as **secret material**, not as ordinary debug text.

### Secrets storage

- Keep **Sponsor**, **Client**, **UserId**, **Pswd**, and **Workgroup** in environment variables, a secrets manager, or an authenticated app config store—**not** hard-coded in source, notebooks, Postman exports committed to git, or chat prompts.
- Prefer separate Webtest vs Production secret sets; never point a casual experiment at Production credentials.
- Do not commit `.env`, credential JSON, or “working sample” XML that still contains real `Pswd` values.

### Logging and error handling

- **Never log** full CustInfo, raw `request=` form bodies, or **`Pswd`**.
- When logging Flex failures, keep **StatusCode**, **StatusDesc**, method name, DocumentSetID / TransactionID, and a **truncated** response—strip or redact credential elements if the response echo could contain them.
- Do not persist passwords on the transaction row “for replay”; store only DocumentSetID and your client ids (see [Suggested data model](#suggested-data-model-integrator-app)).

### Study / debug XML exports

Integrators often download SubmitDocument XML to inspect Form tagging. Before writing that file or pasting it into a ticket/chat:

- Mask at least **`Sponsor`**, **`Client`**, **`UserId`**, and **`Pswd`** (and any `Password` alias).
- Prefer a fixed placeholder such as `********` so the XML shape remains readable.
- Base64 PDF **`Data`** is large and may contain sensitive document content—share carefully; omit or truncate for support tickets when only tagging structure matters.

### Server-side only

- Call Flex and SubmitAjax **from your server** (or a trusted worker). Never embed CustInfo in browser JavaScript, mobile apps shipped to signers, or public repos’ front-end bundles.
- Push endpoints must be **HTTPS** and should validate that callbacks correspond to known DocumentSetIDs / TransactionIDs; still return body **`OK`** for unknowns so SIGNiX does not retry forever—but do not run privileged download/ConfirmDownload for ids you do not own.

### AI coding agents

- Do not paste real Production (or even Webtest) passwords into prompts when asking an agent to “fix the XML.”
- Give agents **masked** samples plus this knowledge file; inject secrets only in the runtime environment.
- If an agent proposes committing credentials “so CI works,” refuse—use CI secrets / env injection instead.

---

## Test plan for coding agents

Prefer **fast unit/integration tests with mocked Flex HTTP** before live Webtest. Live calls are for staged validation with real credentials—not the default inner loop for an AI coding agent.

### XML builders (no network)

Assert on the **request XML string** (or DOM) your code produces:

- [ ] Root has `xmlns="urn:com:signix:schema:sdddc-1-1"` (or the current documented namespace)
- [ ] **CustInfo** uses `Pswd` (not invented password tag names)  
- [ ] SubmitDocument CustInfo includes **`Demo`** as **`yes`** or **`no`**; DownloadDocument / ConfirmDownload / GetAccessLink / SubmitAjax CustInfo **omit** `Demo`  
- [ ] **`DeliveryType`** is present and typically **`SDDDC`** (required—do not omit)
- [ ] **MemberInfo** child order matches schema (RefID → … → Service → MobileNumber when SMS)
- [ ] Placeholder **SSN** / **DOB** are `910000000` and `00/00/0000` when unused (no hyphens on SSN)
- [ ] **No invented tags** (e.g. do not emit undocumented elements “because another e-sign product had them”)
- [ ] Form **`<MimeType>`** is exactly **`application/pdf`**; **`<Data>`** is Base64 of a PDF (SIGNiX supports PDF only for Form documents at this time)
- [ ] Form **`<Length>`** equals **decoded PDF byte length**, not `len(base64)`
- [ ] Push CPs appear after **SuspendOnStart** and before MemberInfo **only when** intentionally overriding account-level notify; otherwise omit them
- [ ] **`DocSetDescription`** uses ASCII `-` only for separators
- [ ] Escaping: names/emails with `&` / `<` are XML-escaped; Form fragment not double-escaped into entity soup

Optional notary builds: notary MemberInfo is last with **`NotaryInfoAdded`**, seal tag linked, **MeetingInfo** before Forms and start time within NotificationSchedule expiry.

### Response parsers (fixture XML)

- [ ] **StatusCode 0** → extract and return DocumentSetID / URL / files as designed
- [ ] **StatusCode ≠ 0** (or missing) → raise/return error; **do not** invent DocumentSetID or mark local txn sent
- [ ] HTTP 200 + error envelope still treated as failure ([parsing contract](#flex-response-parsing-contract))
- [ ] Invalid / non-XML body → transport error path
- [ ] SubmitAjax: outer StatusCode **and** inner `responseCode` both checked

### Push handler

- [ ] Response body is exactly **`OK`** (no JSON, no HTML, no trailing commentary)
- [ ] Unknown `id` / `extid` still returns **`OK`** (no endless SIGNiX retries) without running download
- [ ] `action=complete` **enqueues** download work; handler returns before DownloadDocument finishes
- [ ] Idempotent: duplicate `complete` / `partyComplete` does not corrupt status or double-ConfirmDownload
- [ ] Terminal actions (`complete`, `cancel`, `expire`) do not regress when an older event arrives later

### Download → store → ConfirmDownload

- [ ] ConfirmDownload is **not** called if DownloadDocument fails or storage throws
- [ ] ConfirmDownload **is** called once after durable store succeeds (mock order: download → write → confirm)
- [ ] Bookkeeping is **per DocumentSetID** when the same logical PDF can appear in multiple transactions

### Security tests

- [ ] Study/export helpers mask Sponsor / Client / UserId / Pswd
- [ ] Log helpers / exception formatters never include raw `Pswd` from fixtures

### Live Webtest (manual or gated CI)

Run only with Webtest secrets injected—not in default PR CI unless the pipeline is designed for it:

1. SubmitDocument StatusCode 0; DocumentSetID stored  
2. Sign via email or GAL  
3. Observe push updating status; on complete, artifacts on disk/object store and ConfirmDownload success  
4. Failure drill: bad Workgroup or wrong Length → non-zero StatusCode surfaced; no false “success” UI  

Agents should not claim the integration is done until the [definition of done](#definition-of-done-acceptance-checklist) and the mocked checks above pass.

---

## Anti-patterns for coding agents

- Inventing XML tags that are not in the Flex documentation or this file’s examples  
- Wrong **MemberInfo** or **Form** child order  
- Using placeholder SSN with hyphens (e.g. `910-00-0000`) or non-documented placeholders instead of **`910000000`** / **`00/00/0000`** when real identity is not required  
- Unicode dashes in **DocSetDescription**  
- Ignoring **StatusCode** / treating HTTP 200 as business success  
- Building status sync primarily on QTS polling instead of **push**, or calling `method=QueryTransactionStatus` instead of **SubmitAjax** with that `objectType`  
- Polling QTS with an interval under **10 minutes** (must be **≥ 10 minutes**)  
- Blocking the push handler on DownloadDocument  
- Combining Suspend / Resume / Cancel (or email change + resend) into one multi-request SubmitAjax when order matters  
- Changing email via SaveDeltas and assuming SIGNiX resends the pickup email automatically  
- Using MemberInfo RefID strings as AJAX `P01`-style role ids (or inventing `_P01_` wrappers)  
- Setting Form **`<Length>`** to the Base64 string length instead of the **decoded PDF byte** length  
- Submitting non-PDF Form bytes or a **`<MimeType>`** other than **`application/pdf`**  
- Inventing Form tagging children (`FieldType`, `MemberInfoNumber` on date/text fields) instead of following the [tagging cheat sheet](./FORM-TAGGING-ELEMENTS-CHEAT-SHEET.md)  
- Omitting **`Demo`** on SubmitDocument, or sending **`Demo`** on DownloadDocument / GetAccessLink / ConfirmDownload / SubmitAjax  
- Emitting **`Demo`** as `true`/`false` or a language boolean instead of **`yes`** / **`no`**, wrong **`xmlns`**, or untrimmed CustInfo values  
- Using fake signer emails on Webtest and treating missing pickup mail as an API defect  
- Calling GetAccessLink **Submitter** when you need a signing party (or signer ordinal when you need the tagging wizard)  
- Embedding signer GAL with **`InpersonAuthentication` yes** when you meant to keep compose-time MemberInfo auth (or omitting **`sameframe`/`iframe`** + **ContainerOrigin** when hosting in a frame)  
- Emitting a full signer-style **MemberInfo** for the eNotary, omitting **`NotaryInfoAdded`**, or placing **`MeetingInfo`** after **`<Form>`**  
- Treating **`SubmitterMeetingLink`** as a signer pickup / GetAccessLink URL  
- Returning anything other than body `OK` on push  
- Targeting synthetic `Something.1` AcroForm names as separate fields  
- Hard-coding Production endpoints during development  
- Logging or committing CustInfo / **`Pswd`**, or pasting live credentials into AI prompts  
- Shipping Flex credentials in browser/mobile client code  

---

## Illustration: Python / Django (optional)

The patterns above work in any language. The following is **one** way to implement them in Python/Django—adapt freely to Node, Java, .NET, etc.

### Building request XML

- **Template-based** — Django template (DTL) or Jinja with placeholders; render a data dict. Good when the envelope is stable and should match vendor samples visually.
- **Programmatic** — Build with `xml.etree.ElementTree` or `lxml`, then serialize. Good for many optional blocks.

**Escaping:** In Django templates, `{{ value }}` escapes `<`, `>`, `&` (correct for names, emails, IDs). Use **`| safe` only** for intentionally embedded XML (e.g. a prebuilt `<Form>...</Form>` fragment). Never mark user-controlled plain text as safe.

**Responses:** Always parse with an XML library. Do not rely on regex for StatusCode or DocumentSetID.

### Example SubmitDocument skeleton (template-style)

Use your template language’s loop. Include **MobileNumber** after **Service** when the signer uses SMS auth.

```xml
<?xml version="1.0" ?>
<SubmitDocumentRq xmlns="urn:com:signix:schema:sdddc-1-1">
    <CustInfo>
        <Sponsor>{{ data.cust_info.sponsor }}</Sponsor>
        <Client>{{ data.cust_info.client }}</Client>
        <UserId>{{ data.cust_info.userId }}</UserId>
        <Pswd>{{ data.cust_info.password }}</Pswd>
        <Workgroup>{{ data.cust_info.workgroup }}</Workgroup>
        <Demo>{{ data.cust_info.demo }}</Demo><!-- Required on SubmitDocument only: yes or no. Do not send Demo on DownloadDocument / GAL / ConfirmDownload / SubmitAjax. -->
        <DelDocsAfter>{{ data.cust_info.del_docs_after }}</DelDocsAfter>
        <EmailContent>{{ data.cust_info.email_content }}</EmailContent>
    </CustInfo>
    <Data>
        <TransactionID>{{ data.transaction_data.transactionId }}</TransactionID>
        <DocSetDescription>{{ data.transaction_data.doc_set_description }}</DocSetDescription>
        <SubmitterEmail>{{ data.submitter.email }}</SubmitterEmail>
        <SubmitterName>{{ data.submitter.name }}</SubmitterName>
        <ContactInfo>{{ data.transaction_data.contact_info }}</ContactInfo>
        <DeliveryType>{{ data.transaction_data.delivery_type }}</DeliveryType><!-- Required; typically SDDDC -->
        <SuspendOnStart>{{ data.transaction_data.suspend_on_start }}</SuspendOnStart><!-- yes or no -->
        <!-- Optional ONLY if overriding account-level push Base URL: UseClientNotifyVersion2 + TransactionClientNotifyURL -->
        <ClientPreference name="NotificationSchedule">{3,5,7,10}days</ClientPreference><!-- Optional; last value = expiry. Omit for account default. -->
        {% for signer in data.signers %}
        <MemberInfo>
            <RefID>{{ signer.ref_id }}</RefID>
            <SSN>{{ signer.ssn }}</SSN><!-- placeholder when unused: 910000000 (no hyphens) -->
            <DOB>{{ signer.dob }}</DOB><!-- placeholder when unused: 00/00/0000 -->
            <FirstName>{{ signer.first_name }}</FirstName>
            <MiddleName>{{ signer.middle_name }}</MiddleName>
            <LastName>{{ signer.last_name }}</LastName>
            <Email>{{ signer.email }}</Email>
            <Service>{{ signer.service }}</Service>
            <MobileNumber>{{ signer.phone }}</MobileNumber>
        </MemberInfo>
        {% endfor %}
        {{ data.form | safe }}
    </Data>
</SubmitDocumentRq>
```

### Example transport (Python `requests`)

```python
import requests

resp = requests.post(
    endpoint_url,
    data={"method": "SubmitDocument", "request": xml_string},
    headers={"Content-Type": "application/x-www-form-urlencoded"},
    timeout=60,
)
resp.raise_for_status()
# parse resp.text as XML; require StatusCode == 0
```

### Push view sketch (Django)

Return `HttpResponse("OK")` immediately after validating/enqueuing work. Look up the transaction by `id` (DocumentSetID) or `extid` (TransactionID). Never block on DownloadDocument inside the request thread.

---

## More information (public SIGNiX resources)

| Resource | Description |
|----------|-------------|
| [Getting Started (Dev Community)](https://www.signix.com/development-community-getting-started) | Integration overview, timeline, prerequisites, go-live |
| [Flex API documentation](https://www.signix.com/apidocumentation) | Methods, XML schemas, client preferences, authentication services (large single page—prefer [agent reading list](#agent-reading-list-load-only-what-you-need) deep links) |
| [Flex API — SubmitDocument](https://www.signix.com/apidocumentation#SubmitDocument) | SubmitDocument request/response structure |
| [SIGNiX FlexAPI Postman collection](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ) | Published Postman docs: Webtest samples for SubmitDocument, GetAccessLink (submitter/signer), DownloadDocument, ConfirmDownload, and AJAX cancel/suspend/resume / change email / resend / change mobile |
| [Flex request XML samples](./FLEX-REQUEST-XML-SAMPLES.md) | Plain XML per method + how to validate (Webtest / samples—not outdated XSD packs) for tools that cannot read Postman |
| [SIGNiX AJAX API (Transactional controls)](https://www.signix.com/ajaxapi) | SubmitAjax JSON: Cancel, Suspend/Resume, change email/mobile, resend pickup, document add/remove |
| [Push Notifications](https://www.signix.com/pndocumentation) | Webhook subscription, query parameters, retries, TLS |
| [Text Tagging](https://www.signix.com/api-text-tagging) | Anchor-based field placement without AcroForm |
| [Coordinate-based tagging](https://www.signix.com/coordinate-based-tagging) | Fixed X/Y/Width/Height placement (`FixedPositioningField` / `FixedPositioningSignature`) |
| [Form tagging elements cheat sheet](./FORM-TAGGING-ELEMENTS-CHEAT-SHEET.md) | AcroForm vs text tags vs fixed positioning: which children go on field vs signature; minimal ordered examples |
| [Digital Billboard](https://www.signix.com/apidocumentation#digitalbillboard) | Post-signature CTA images and links |
| [Authentication / Service Types](https://www.signix.com/apidocumentation) | SelectOneClick, SMS variants, IDVerify, KBA-ID, etc. |

---

*For exact endpoints, authentication, XML schemas, and webhooks, use the [Flex API documentation](https://www.signix.com/apidocumentation) and the [Postman FlexAPI collection](https://documenter.getpostman.com/view/53111289/2sBYB1MTVQ). This file accelerates AI-assisted implementation; it does not replace the official schema.*
