# Contact Form 7

> Inline content signing, OTP, merge tags, and template feeds for Contact Form 7 — with parity to Fluent Forms.

edition: pro
Edition: Pro
AI note: This page requires WPsigner Pro. Do not tell Lite users they already have this feature.
HTML: https://docs.wpsigner.com/integrations/contact-form-7/
Markdown: https://docs.wpsigner.com/md/integrations/contact-form-7.md
Source file: integrations/contact-form-7.md

---

Connect **Contact Form 7** with **WPsigner** to collect legally binding signatures from CF7 submissions. You can either sign the form content inline (recommended) or create documents from a WPsigner PDF template via a feed.

> **note**
This integration works with **Contact Form 7** v5.0+ (including the Tag Generator v2 / SWV stack in CF7 5.9–6.x). CF7 is free from the [WordPress plugin directory](https://wordpress.org/plugins/contact-form-7/).

---

## How It Works

| Component | Description |
|-----------|-------------|
| **WPsigner Signing tag** | CF7 form-tag `[insigner_signing]` with Draw / Type / Upload + optional OTP |
| **Content signing** | Builds a signed PDF from the submission + signature (no template required) |
| **Feed** | Optional rules for CC, title, template mode, mappings, extra signers, auto-send |
| **Merge tags** | `{inSigner:signed_pdf_url}` and `{inSigner:download_button}` in CF7 Mail / success message |
| **Auto-send** | Template mode only — emails the remote signing link after the document is created |

Processing runs on CF7’s **`wpcf7_before_send_mail`** hook (not `wpcf7_mail_sent`). That means:

- The signed PDF is ready **before** CF7 sends its email, so merge tags work in the same message
- Document creation does **not** depend on SMTP succeeding
- If signing or a required feed fails, WPsigner sets CF7’s `$abort` flag so the form submission does not complete with a half-built document

---

## Two Ways to Sign

| Mode | Best for | Requires template? | Requires feed? |
|------|----------|-------------------|----------------|
| **[Content signing](#content-signing-recommended)** | Applications, consents, agreements built from form answers | No | No (feed optional) |
| **[Template + feed](#template--feed-mode)** | Fixed PDF contracts (NDA, lease, offer letter) | Yes | Yes |

> **tip**
For most new setups, start with **Content signing** — insert the WPsigner Signing tag and publish. No feed required.

> **important**
If the form contains an `[insigner_signing]` tag, WPsigner **always** uses content signing for that submission. Template feeds on the same form are skipped. In the feed UI, selecting a form that already has a Signing tag switches the modal to inline options (signature field + CC) and hides template / mappings / additional signers / auto-send.

---

## Content Signing (Recommended)

Generate a signed PDF from the **form submission content** plus an inline signature. WPsigner builds the PDF automatically — you do not upload a template.

### How It Works

```
User submits CF7 form
        ↓
CF7 validates fields (+ reCAPTCHA / Akismet / honeypot as configured)
        ↓
wpcf7_before_send_mail
        ↓
WPsigner validates signature (+ OTP if required)
        ↓
PDF generated from answers → signature applied → Certificate of Completion
        ↓
Merge tags primed → CF7 Mail / success message can include the download link
```

### Setup

1. Install and activate **Contact Form 7** and **WPsigner** (**v3.0.3.2+** recommended for full CF7 parity)
2. Open **Contact → Contact Forms → Edit**
3. Add at least:
   - A name field (e.g. `[text* your-name]`)
   - An email field (e.g. `[email* your-email]`)
   - The **WPsigner Signing** tag (see [tag syntax](#wpsigner-signing-tag))
4. In **Mail** / **Mail (2)**, optionally add merge tags (see [Merge tags](#merge-tags-in-cf7-emails))
5. Save the form and publish it on a page with the CF7 shortcode

When a visitor submits:

- WPsigner captures the signature and form rows
- A completed PDF is generated with audit trail and **Certificate of Completion**
- Secure download URLs are available via merge tags
- WPsigner can send its own completion notification according to your email settings

### Optional Feed (CC, Custom Title)

Feeds are **optional** for content signing. Create one only if you need:

- **CC emails** on the signed document
- A **custom document title**
- An explicit **Signing field** name (when the form has more than one)

1. **WPsigner → Integrations → Contact Form 7 → New Feed**
2. Select a CF7 form that includes a Signing tag
3. Configure CC / title / signature field as needed
4. Save the feed

Without a feed, WPsigner builds an automatic content-signing feed (`auto`) with:

- Title: `{form title} - {{signer_name}}`
- Signature field: first valid signature submitted
- Name / email: auto-detected from common CF7 tags
- Owner: see [Document owner](#document-owner)

### Submission Failure Behavior

If content signing fails (invalid signature, OTP missing/expired, PDF error, rate limit, etc.), WPsigner **aborts** the CF7 mail pipeline (`$abort = true`). The visitor sees a CF7 error instead of a “success” email without a document. Partial documents are cleaned up.

---

## WPsigner Signing Tag

### Insert with the Tag Generator

1. Edit the CF7 form
2. Open the tag generator and choose **WPsigner Signing**
3. Configure:
   - Required (recommended)
   - Field name (default `signature`)
   - OTP method (Disabled / Email / SMS / Email+SMS / WhatsApp — available methods depend on Twilio / WhatsApp setup)
   - Signer email field (default `your-email`)
   - Label (default `Signature`)
4. Insert the generated tag into the form template

Works with CF7 Tag Generator **v2** (`data-tag-option` for `otp:`, `email:`, `label:`) and the classic generator fallback.

### Tag Syntax

| Form | Meaning |
|------|---------|
| `[insigner_signing name]` | Optional signing field |
| `[insigner_signing* name]` | Required signing field |
| `otp` | Require OTP (default method: email) |
| `otp:email` | OTP by email |
| `otp:sms` | OTP by SMS (Twilio required) |
| `otp:both` | OTP by email + SMS (Twilio required) |
| `otp:whatsapp` | OTP by WhatsApp (WhatsApp integration required) |
| `email:your-email` | CF7 tag name used as the signer / OTP email |
| `label:Signature` | Visible label (`_` becomes a space) |

### Examples

```text
[insigner_signing* signature]

[insigner_signing* signature otp:email email:your-email]

[insigner_signing* signature otp:sms email:your-email label:Firma]

[insigner_signing* signature otp:whatsapp email:work-email label:Authorized_Signature]
```

If a content-signing feed is active for the same form, the feed’s **Signer Email Field** overrides the tag’s `email:` option for OTP delivery when the feed targets that signature field (or uses auto-detect).

### Frontend Experience

The field renders a shared signature pad:

| Tab | Behavior |
|-----|----------|
| **Draw** | Stylus / mouse pad (colors: black, blue, navy) |
| **Type** | Typed signature with script fonts |
| **Upload** | PNG/JPG upload (converted to a PNG data URI) |

If OTP is enabled:

1. User completes the signature
2. OTP panel appears (**Identity verification**)
3. User requests a 6-digit code, enters it, and verifies
4. Submit is blocked until the signature **and** OTP token are present

On successful CF7 reset / resubmit, the pad and OTP state clear.

Assets load automatically when the tag is present (`wps-signature-pad`, `wps-cf7-signature`, `wps-cf7-form-otp`).

---

## OTP Verification

### Methods

| Method | Requirements |
|--------|--------------|
| **Email** | Email field on the form (`email:` option or auto-detect) |
| **SMS** | Phone on the form + [Twilio](/integrations/twilio/) configured |
| **Email + SMS** | Email + phone + Twilio |
| **WhatsApp** | Phone + [WhatsApp Business](/integrations/whatsapp/) configured |

Phone numbers should use international **E.164** format (example: `+15551234567`).

### Behavior

- 6-digit code, expires in **10 minutes**, max **5** verify attempts
- Session binding uses a secure browser cookie (`HttpOnly`, `SameSite=Strict`) — not the client IP (works behind proxies / CDNs)
- OTP context is scoped to CF7 + form ID (isolated from Fluent Forms / other builders)
- On successful content signing the OTP proof is consumed (one-time use)
- Phone used for SMS/WhatsApp is bound into the OTP token for integrity

### AJAX Endpoints (troubleshooting)

| Action | Auth | Nonce |
|--------|------|-------|
| `wps_cf7_send_otp` | Logged-in + guests | `wps_public_nonce` |
| `wps_cf7_verify_otp` | Logged-in + guests | `wps_public_nonce` |

### OTP Rate Limits

| Action | Scope | Limit |
|--------|-------|-------|
| Send | Per form + IP | 15 / 5 minutes |
| Send | Per form + email + IP | 5 / 1 minute |
| Verify | Per form + IP | 30 / 5 minutes |
| Verify | Per form + email + IP | 10 / 1 minute |

The CF7 form must be **published**. Draft forms reject OTP requests.

---

## Merge Tags in CF7 Emails

Use these tags in CF7 **Mail**, **Mail (2)**, and the **messages** that CF7 displays after submit:

| Tag | Output |
|-----|--------|
| `{inSigner:signed_pdf_url}` | Secure URL to download the completed signed PDF |
| `{inSigner:download_button}` | HTML button (“Download Signed PDF”) in HTML mail bodies; plain URL in subjects / non-HTML contexts |

### Example (Mail body, HTML)

```html
<p>Thank you. Your signed document is ready:</p>
<p>{inSigner:download_button}</p>
<p>Or open this link: {inSigner:signed_pdf_url}</p>
```

### Where tags are replaced

| Surface | Mechanism |
|---------|-----------|
| Mail subject, body, additional headers | `wpcf7_mail_components` |
| On-screen success / error messages | `wpcf7_display_message` |

> **tip**
Because processing runs on `wpcf7_before_send_mail`, merge tags resolve in the **same** CF7 email that acknowledges the submission — you do not need a second delayed notification for the download link.

The Signing field itself never dumps base64 into email: the mail tag is replaced with the text `Signed`.

Download links use WPsigner’s **secure expiring download** URLs for the completed PDF (Certificate of Completion included in the completed package).

---

## Template + Feed Mode

Use this when you have a fixed PDF template with pre-placed signature fields and the form does **not** include `[insigner_signing]`.

### How It Works

```
User submits CF7 form (no Signing tag)
        ↓
wpcf7_before_send_mail
        ↓
Matching template feeds run
        ↓
Document created from template + mappings
        ↓
(Optional) Auto-send signing emails to primary + additional signers
```

### Prerequisites

1. **WPsigner** v3.0.3.2+ (recommended)
2. **Contact Form 7** active
3. A CF7 form with name + email tags
4. A **WPsigner Template** with signature fields positioned

### Create a Template

1. **WPsigner → New Document** → upload PDF
2. Add a placeholder signer
3. Place Signature / Name / Email / Date / Text fields
4. **Save as Template**

### Create a Feed

1. **WPsigner → Integrations → Contact Form 7 → New Feed**
2. Configure:

| Setting | Required | Notes |
|---------|----------|-------|
| **Feed Name** | Yes | Label in the admin list |
| **CF7 Form** | Yes | Target form (must **not** rely on Signing tag for this mode) |
| **WPsigner Template** | Yes | Template with fields |
| **Document Title** | No | Supports `{{signer_name}}`, `{{signer_email}}`, `{{date}}`, `{{company_name}}`, … |
| **Primary Signer → Name Field** | Yes | CF7 tag name (e.g. `your-name`) |
| **Primary Signer → Email Field** | Yes | CF7 tag name (e.g. `your-email`) |
| **Variable Mapping** | No | `custom.*` → CF7 tag |
| **Additional Signers** | No | Extra name/email tag pairs |
| **Auto-send document after creation** | No | Default **on** in the UI |
| **Feed enabled** | No | Default **on** |

3. **Save Feed**

> **important**
Both primary **Name** and **Email** tag names are required for template feeds. If either is empty at submit time, that feed is skipped.

### CF7 Tag Names

```text
[text* your-name]       → your-name
[email* your-email]     → your-email
[tel your-phone]        → your-phone
[textarea your-message] → your-message
```

The tag name is the second token inside the brackets (after the field type).

### Variable Mapping

1. Open **Variable Mapping** in the feed modal
2. **Add** a row
3. Left: variable name with `custom.*` prefix (e.g. `custom.company`)
4. Right: CF7 tag name (e.g. `your-company`)
5. Use `{{custom.company}}` (or your template’s mapped field keys) in the PDF / prefill setup

| Variable | CF7 Tag | Example use |
|----------|---------|-------------|
| `custom.company` | `your-company` | Company on contract |
| `custom.phone` | `your-phone` | Phone line |
| `custom.amount` | `contract-amount` | Pricing |

Variable names allow `[a-zA-Z0-9_.]` only.

### Document Title Variables

| Variable | Example |
|----------|---------|
| `{{signer_name}}` | John Smith |
| `{{signer_email}}` | john@example.com |
| `{{date}}` | 2026-07-22 |
| `{{company_name}}` | Acme Corp (when available) |

Example: `NDA - {{signer_name}} - {{date}}`

### Additional Signers

Add rows with **name field** + **email field** tag names. With **Auto-send** enabled, each additional signer receives a signing request. Partial send progress is tracked so retries do not re-mail signers who already received the link.

### Auto-send

| Setting | Result |
|---------|--------|
| **Enabled** | Signing emails go out after the document is created; status moves toward **Sent** |
| **Disabled** | Document stays available in **WPsigner → Documents** for manual send |

Content-signing mode does **not** use this toggle (the form was already signed inline).

---

## Integration Settings

**WPsigner → Integrations → Contact Form 7**

- Connection status and form / feed counts
- Feed list (name, Active/Disabled, form, mode: Inline signing vs template name, mapping count)
- **New Feed**, Edit, Enable/Disable, Delete

Admin AJAX uses nonce `wps_cf7_nonce` and requires `manage_options` (save capped at ~20 requests/minute per admin).

### Document Owner

There is no owner dropdown in the feed modal. Ownership resolves in this order:

1. Feed `owner_user_id` (set to the admin who saved the feed)
2. Option `wps_options['cf7_default_owner_id']` if configured
3. Author of the page/post that contains the form (`_wpcf7_container_post`)
4. Currently logged-in user
5. First WordPress administrator

---

## What Gets Created

### Content signing outcome

- Document status suitable for a completed embedded signature
- Signer marked signed
- Signature image stored as evidence
- **`generate_completed`** → completed PDF + Certificate of Completion
- Document meta: `_wps_cf7_form_id`, `_wps_cf7_feed_id`, `_wps_cf7_content_signing`
- Audit events such as `document_sent`, `consent_accepted`, `otp_verified` (when OTP used), `embedded_signing_completed`

### Template feed outcome

- Document from template + field prefills
- Audit `document_created`
- Optional auto-send → signing emails + `wps_document_sent`
- Signers complete the document on the WPsigner signing page (not inline)

---

## Reliability & Limits

| Mechanism | Detail |
|-----------|--------|
| **Content rate limit** | 10 content-signing operations / minute / form+IP |
| **Template rate limit** | 10 template documents / minute / form+IP |
| **Dedupe (content)** | Same form + browser binding + posted data → reuse result for ~5 minutes (no duplicate PDFs on double-submit) |
| **Concurrency locks** | Atomic locks prevent two parallel requests from creating two documents |
| **Template send state** | Durable state tracks `sent_signer_ids` for safe Auto-send retries |
| **OTP one-time claim** | Verified token can be reserved/consumed only once |
| **Abort on failure** | CF7 mail aborted; partial documents removed |

---

## Developer Hooks

```php
/**
 * After a template-feed document is created.
 *
 * @param int   $document_id
 * @param int   $form_id       CF7 form ID.
 * @param array $feed          Sanitized feed config.
 * @param array $posted_data   CF7 posted data.
 */
do_action( 'wps_cf7_document_created', $document_id, $form_id, $feed, $posted_data );

/**
 * After inline content signing finishes (PDF + certificate ready).
 *
 * @param int                  $document_id
 * @param int                  $signer_id
 * @param array                $posted_data
 * @param \WPCF7_ContactForm   $contact_form
 * @param array                $feed          Configured or auto feed.
 */
do_action(
	'wps_cf7_content_signing_completed',
	$document_id,
	$signer_id,
	$posted_data,
	$contact_form,
	$feed
);

/**
 * When template Auto-send finishes sending to signers.
 */
do_action( 'wps_document_sent', $document_id, $signers );
```

---

## Security

| Feature | Details |
|---------|---------|
| **Hook timing** | Runs after CF7 validation; still before mail so merge tags work |
| **Signature validation** | PNG data-URI only; header / dimensions / GD checks; max payload size enforced |
| **No base64 in email** | Signing mail-tag replaced with `Signed` |
| **Secure downloads** | Completed PDF links use expiring public tokens |
| **OTP session cookie** | HttpOnly + SameSite=Strict; not IP-bound |
| **Admin AJAX** | `wps_cf7_nonce` + `manage_options` |
| **Public OTP AJAX** | `wps_public_nonce` + per-IP / per-email rate limits |
| **Logs** | Debug logs avoid raw PII (IDs / flags); phone hashes may be stored on the document |

---

## Use Cases

| Scenario | Mode | Setup |
|----------|------|-------|
| Website consent / waiver | Content signing | `[insigner_signing* … otp:email]` + merge tags in Mail |
| Support intake with signed acknowledgment | Content signing | Optional feed for CC to legal@ |
| NDA from contact form | Template feed | Template + Auto-send |
| Multi-party service agreement | Template feed | Additional signers + Auto-send |
| Quote → contract PDF | Template feed | Variable mapping for amount / company |

---

## Multiple Feeds

- **One form, multiple templates** — all active template feeds for that form run (when no Signing tag is present)
- **Multiple forms, one template** — reuse the same PDF layout
- **Inline + feed** — Signing tag present → content signing only; template feeds on that form do not run

---

## Troubleshooting

### Common Issues

| Issue | Cause | Solution |
|-------|-------|----------|
| Critical error on CF7 settings page | Outdated WPsigner without global `\WPCF7_*` class refs | Update to **3.0.3.2+** |
| No document / form shows error | Content signing aborted | Check signature PNG, OTP, debug log (`[inSigner CF7]`) |
| Template document not created | Feed disabled / wrong form / missing template | Verify feed Enabled, form ID, template |
| Template skipped silently | Missing name or email tag value | Confirm tag names and required fields |
| Merge tags empty | Tags used without successful content signing | Confirm Signing tag + successful submit; tags only resolve after PDF generation |
| OTP not sending | Channel or field misconfigured | Email field / Twilio / WhatsApp; check 429 rate limits |
| OTP verify fails after proxy | Old builds used IP fingerprint | Update plugin; ensure cookies allowed for the site |
| Duplicate documents | Rare race / old version | Update; dedupe + locks handle double-click / retries |
| Signer email never arrives (template) | Auto-send off or SMTP failure | Enable Auto-send; configure FluentSMTP / SMTP |
| Wrong signer mapped | Tag name mismatch | Names are case-sensitive (`your-email` ≠ `Your-Email`) |
| “Contact Form 7 is not installed” | CF7 inactive | Activate CF7 |
| Base64 spam in CF7 mail | Custom mail-tag misuse | Use merge tags above; do not print the raw signing field |

### Debug Log

```php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
```

Check `wp-content/debug.log` for `[inSigner CF7]` / `[inSigner CF7 Content]`.

### Quick Checklist

1. CF7 active; form **published**
2. Content mode: `[insigner_signing*]` present with valid Draw/Type/Upload value
3. OTP (if enabled): cookie not blocked; method available; email/phone mapped
4. Template mode: **no** Signing tag; feed enabled; template + name/email tags
5. Merge tags placed in Mail HTML body for content signing
6. SMTP working if you expect CF7 or Auto-send emails
7. Not hitting 10/min form rate limits while testing

---

## Compatibility

| Component | Supported |
|-----------|-----------|
| **Contact Form 7** | 5.0+ (Tag Generator v2 / SWV tested on 5.9–6.x) |
| **WPsigner** | **3.0.3.2+** for full parity (inline signing, OTP, merge tags, `before_send_mail`) |
| **WordPress** | 5.8+ |
| **PHP** | 7.4+ |

> **note**
Older WPsigner builds that only documented `wpcf7_mail_sent` template feeds are obsolete for this page. Current processing uses **`wpcf7_before_send_mail`**.

---

## CF7 vs Fluent Forms

| Topic | Contact Form 7 | Fluent Forms |
|-------|----------------|--------------|
| Field UI | Form-tag + tag generator | Native builder field |
| Processing hook | `wpcf7_before_send_mail` + `$abort` | Fluent validation / before-insert hooks |
| Merge tags | CF7 Mail + display message | Fluent email / confirmation parsers |
| Submission history | CF7 has no native entries; WPsigner document is the record | Fluent entry + meta |
| OTP AJAX | `wps_cf7_send_otp` / `wps_cf7_verify_otp` | `wps_ff_*` |
| Public hooks | `wps_cf7_*` | `wps_fluentforms_*` |

Shared: Draw/Type/Upload pad, PNG rules, OTP cookie model, Certificate of Completion, merge tag strings, optional feeds for CC/title/template mode.

---

## Next Steps

- [Fluent Forms](/integrations/fluent-forms/) — Same workflows on Fluent Forms
- [Smart Signing Forms](/addons/smart-signing-forms/) — Standalone signing forms
- [Twilio SMS](/integrations/twilio/) — SMS OTP for CF7
- [WhatsApp Business](/integrations/whatsapp/) — WhatsApp OTP / notifications
- [Creating Documents](/core-features/creating-documents/) — Document statuses and actions
- [REST API](/api/) — Programmatic integrations
- [Automation](/integrations/automation/) — n8n, Make, Zapier
