# WPsigner Integrations Pack

> Forms, community, CRM, messaging, cloud storage, automation, payments, and LMS.

Generated for AI assistants from the official WPsigner docs.
HTML guide: https://docs.wpsigner.com/support/use-with-ai/
Pages in this pack: 30

## Suggested prompt

```text
You are helping me configure and use WPsigner, a self-hosted electronic-signature plugin for WordPress.
Use ONLY the documentation below as your source of truth.
Each page header includes edition: lite, pro, or both. Never invent Pro-only features for Lite.
If something is not covered, say so clearly and ask for the missing detail.
Prefer exact admin menu paths, shortcodes, endpoints, and settings names from the docs.
```

## Contents

1. [Integrations](https://docs.wpsigner.com/md/integrations.md) — https://docs.wpsigner.com/integrations/
2. [Amazon S3](https://docs.wpsigner.com/md/integrations/amazon-s3.md) — https://docs.wpsigner.com/integrations/amazon-s3/
3. [n8n & Make Automation](https://docs.wpsigner.com/md/integrations/automation.md) — https://docs.wpsigner.com/integrations/automation/
4. [Cloudflare R2](https://docs.wpsigner.com/md/integrations/cloudflare-r2.md) — https://docs.wpsigner.com/integrations/cloudflare-r2/
5. [Contact Form 7](https://docs.wpsigner.com/md/integrations/contact-form-7.md) — https://docs.wpsigner.com/integrations/contact-form-7/
6. [Didit.me — Identity Verification (KYC)](https://docs.wpsigner.com/md/integrations/didit.md) — https://docs.wpsigner.com/integrations/didit/
7. [Dropbox](https://docs.wpsigner.com/md/integrations/dropbox.md) — https://docs.wpsigner.com/integrations/dropbox/
8. [Elementor Forms](https://docs.wpsigner.com/md/integrations/elementor-forms.md) — https://docs.wpsigner.com/integrations/elementor-forms/
9. [Fluent Community](https://docs.wpsigner.com/md/integrations/fluent-community.md) — https://docs.wpsigner.com/integrations/fluent-community/
10. [Fluent Forms Integration](https://docs.wpsigner.com/md/integrations/fluent-forms.md) — https://docs.wpsigner.com/integrations/fluent-forms/
11. [FluentCRM](https://docs.wpsigner.com/md/integrations/fluentcrm.md) — https://docs.wpsigner.com/integrations/fluentcrm/
12. [Google Drive Integration](https://docs.wpsigner.com/md/integrations/google-drive.md) — https://docs.wpsigner.com/integrations/google-drive/
13. [Gravity Forms](https://docs.wpsigner.com/md/integrations/gravity-forms.md) — https://docs.wpsigner.com/integrations/gravity-forms/
14. [HubSpot](https://docs.wpsigner.com/md/integrations/hubspot.md) — https://docs.wpsigner.com/integrations/hubspot/
15. [LearnDash](https://docs.wpsigner.com/md/integrations/learndash.md) — https://docs.wpsigner.com/integrations/learndash/
16. [Make (Integromat) Integration](https://docs.wpsigner.com/md/integrations/make.md) — https://docs.wpsigner.com/integrations/make/
17. [n8n Integration](https://docs.wpsigner.com/md/integrations/n8n.md) — https://docs.wpsigner.com/integrations/n8n/
18. [OneDrive](https://docs.wpsigner.com/md/integrations/onedrive.md) — https://docs.wpsigner.com/integrations/onedrive/
19. [Pabbly Connect](https://docs.wpsigner.com/md/integrations/pabbly.md) — https://docs.wpsigner.com/integrations/pabbly/
20. [Pipedrive](https://docs.wpsigner.com/md/integrations/pipedrive.md) — https://docs.wpsigner.com/integrations/pipedrive/
21. [Slack](https://docs.wpsigner.com/md/integrations/slack.md) — https://docs.wpsigner.com/integrations/slack/
22. [Stripe Payments](https://docs.wpsigner.com/md/integrations/stripe.md) — https://docs.wpsigner.com/integrations/stripe/
23. [Microsoft Teams](https://docs.wpsigner.com/md/integrations/teams.md) — https://docs.wpsigner.com/integrations/teams/
24. [Telegram Bot Integration](https://docs.wpsigner.com/md/integrations/telegram.md) — https://docs.wpsigner.com/integrations/telegram/
25. [Twilio SMS](https://docs.wpsigner.com/md/integrations/twilio.md) — https://docs.wpsigner.com/integrations/twilio/
26. [Wasabi](https://docs.wpsigner.com/md/integrations/wasabi.md) — https://docs.wpsigner.com/integrations/wasabi/
27. [WhatsApp Business Integration](https://docs.wpsigner.com/md/integrations/whatsapp.md) — https://docs.wpsigner.com/integrations/whatsapp/
28. [WooCommerce](https://docs.wpsigner.com/md/integrations/woocommerce.md) — https://docs.wpsigner.com/integrations/woocommerce/
29. [WPForms Integration](https://docs.wpsigner.com/md/integrations/wpforms.md) — https://docs.wpsigner.com/integrations/wpforms/
30. [Zapier Integration](https://docs.wpsigner.com/md/integrations/zapier.md) — https://docs.wpsigner.com/integrations/zapier/

---

# Integrations

> Connect WPsigner with forms, messaging apps, CRMs, cloud storage, automation platforms, and e-commerce tools.

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/
Markdown: https://docs.wpsigner.com/md/integrations.md

WPsigner integrates with the tools you already use — from WordPress community and form plugins to CRM, messaging, storage, and automation services. Configure integrations from **WPsigner → More → Integrations** in your WordPress admin.

> **Requires WPsigner Pro**
Third-party integrations are not included in **Lite**. [Upgrade to Pro](https://wpsigner.com/pricing/?utm_source=docs&utm_medium=upgrade&utm_campaign=integrations) · [Lite vs Pro](/getting-started/lite-vs-pro/)

---

## Identity Verification (KYC)

Verify signer identity before they can sign using government-issued ID, selfie matching, and liveness detection.

| Integration | Description | Auth Method |
|-------------|-------------|-------------|
| [Didit.me](/integrations/didit/) | ID document verification with selfie and liveness detection | API Key + Workflow ID |

---

## Community

Give logged-in community members direct access to their signing documents.

| Integration | Description | Auth Method |
|-------------|-------------|-------------|
| [Fluent Community](/integrations/fluent-community/) | Add a Documents to sign link to member profiles and the community header | Direct PHP hooks (no API key) |

---

## Forms

Automatically create signing documents when a form is submitted. Form plugins support **inline content signing** (signature field + PDF + certificate) and/or **template feeds** (map fields, extra signers, auto-send).

| Integration | Description | License Required |
|-------------|-------------|-----------------|
| [Fluent Forms](/integrations/fluent-forms/) | Content signing, OTP, merge tags, and template feeds | Free or Pro |
| [WPForms](/integrations/wpforms/) | Create documents from WPForms submissions | Lite or Pro |
| [Gravity Forms](/integrations/gravity-forms/) | Feed-based with sub-field detection for Name/Address fields | Any license tier |
| [Elementor Forms](/integrations/elementor-forms/) | Auto-discover forms across your Elementor pages | Elementor Pro |
| [Contact Form 7](/integrations/contact-form-7/) | Inline signing tag, OTP, merge tags, and template feeds (parity with Fluent Forms) | Free |

---

## Messaging

Send signing requests, reminders, and completion notifications through messaging platforms. These work alongside email — they don't replace it.

| Integration | Description | Cost |
|-------------|-------------|------|
| [WhatsApp Business](/integrations/whatsapp/) | Send signing links via WhatsApp using the Meta Business API | Per-message (Meta pricing) |
| [Telegram](/integrations/telegram/) | Bot-based notifications with inline signing buttons | Free |
| [Slack](/integrations/slack/) | Channel notifications via Incoming Webhooks with Block Kit formatting | Free |
| [Twilio SMS](/integrations/twilio/) | Text message signing requests to any mobile phone | Per-message (Twilio pricing) |
| [Microsoft Teams](/integrations/teams/) | Adaptive Card notifications in Teams channels | Free |

---

## CRM

Sync signing events with your CRM — create contacts, log activities, and update properties when documents are signed.

| Integration | Description | Auth Method |
|-------------|-------------|-------------|
| [HubSpot](/integrations/hubspot/) | Find/create contacts, log timeline notes, update custom properties | Private App Token |
| [Pipedrive](/integrations/pipedrive/) | Find/create persons, create activities and notes | API Token |
| [FluentCRM](/integrations/fluentcrm/) | Tag contacts and log notes in FluentCRM | Direct PHP hooks (no API key) |

---

## Cloud Storage

Automatically back up signed PDFs to your cloud storage provider when a document is completed.

| Integration | Description | Auth Method |
|-------------|-------------|-------------|
| [Google Drive](/integrations/google-drive/) | One-click OAuth connection, automatic folder creation | OAuth 2.0 |
| [Dropbox](/integrations/dropbox/) | OAuth-based backup to a dedicated WPsigner folder | OAuth 2.0 |
| [OneDrive](/integrations/onedrive/) | Microsoft Graph API with chunked upload for large files | Azure App Registration |
| [Amazon S3](/integrations/amazon-s3/) | Direct S3 bucket upload with IAM credentials | Access Key + Secret |
| [Wasabi](/integrations/wasabi/) | S3-compatible storage with no egress fees | Access Key + Secret |
| [Cloudflare R2](/integrations/cloudflare-r2/) | Zero egress fees with global CDN distribution | Account ID + API Token |

---

## Automation

Connect WPsigner to thousands of apps through automation platforms. Use triggers (document events) and actions (create/send documents) to build custom workflows.

| Integration | Description | Type |
|-------------|-------------|------|
| [Zapier](/integrations/zapier/) | 6,000+ app connections with real-time REST Hook triggers | Cloud (SaaS) |
| [Make](/integrations/make/) | Visual scenario builder with advanced data mapping | Cloud (SaaS) |
| [n8n](/integrations/n8n/) | Self-hosted automation with full data control | Self-hosted |
| [Pabbly Connect](/integrations/pabbly/) | Budget-friendly automation with one-time pricing | Cloud (SaaS) |
| [WPsigner for Uncanny Automator](/addons/uncanny-automator/) | Native WordPress recipes: signing/payment/cloud triggers, prefill, messaging, and signer loops | Official addon ([full guide](/addons/uncanny-automator/)) |
| [n8n & Make Guide](/integrations/automation/) | Step-by-step guide for webhook-based integrations | Reference |

---

## E-Commerce & LMS

Trigger document signing from purchases or course enrollments.

| Integration | Description | Auth Method |
|-------------|-------------|-------------|
| [WooCommerce](/integrations/woocommerce/) | Create documents when orders reach a specific status, with product filtering | Direct PHP hooks |
| [LearnDash](/integrations/learndash/) | Require signed agreements before course enrollment or completion | Direct PHP hooks |

---

## Agency Dashboard (MainWP)

Manage WPSigner contracts from your MainWP Dashboard — linked to Clients and Sites. This is an **official addon** (not a child-site plugin).

| Integration | Description | Docs |
|-------------|-------------|------|
| [WPSigner for MainWP](/addons/mainwp/) | Create, send, remind, and track agency contracts inside MainWP | Full guide |

---

## Security Across All Integrations

Every WPsigner integration follows the same security standards:

| Feature | Implementation |
|---------|---------------|
| **Credential storage** | API keys and tokens encrypted with AES-256-GCM at rest |
| **Access control** | `manage_options` capability required for all settings |
| **CSRF protection** | WordPress nonce verification on every AJAX request |
| **Rate limiting** | Per-user limits on test and save operations |
| **Input validation** | All fields sanitized with appropriate WordPress functions |
| **Error logging** | PII-redacted logs when `WP_DEBUG` is enabled |

---

# Amazon S3

> Store signed documents in Amazon S3 buckets

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/amazon-s3/
Markdown: https://docs.wpsigner.com/md/integrations/amazon-s3.md

Automatically back up signed PDFs to **Amazon S3** when all signatures are complete.

---

## Requirements

- WPsigner **2.1.0+**
- An AWS account with S3 access
- An IAM user with `s3:PutObject`, `s3:GetObject`, `s3:DeleteObject` permissions

---

## Setup

### Step 1: Create an S3 Bucket

1. Go to the [AWS S3 Console](https://s3.console.aws.amazon.com/)
2. Click **Create bucket**
3. Choose a name and region
4. Keep **Block all public access** enabled (recommended)
5. Click **Create bucket**

### Step 2: Create IAM Credentials

1. Go to **IAM → Users → Add user**
2. Create a user with **Programmatic access**
3. Attach a policy with these permissions:

```json
{
    "Version": "2012-10-17",
    "Statement": [{
        "Effect": "Allow",
        "Action": [
            "s3:PutObject",
            "s3:GetObject",
            "s3:DeleteObject"
        ],
        "Resource": "arn:aws:s3:::YOUR-BUCKET-NAME/*"
    }]
}
```

4. Save the **Access Key ID** and **Secret Access Key**

> **important**
Store your Secret Access Key securely. AWS only shows it once during creation. If lost, you must create a new key pair.

### Step 3: Configure WPsigner

1. Go to **WPsigner → Integrations → Amazon S3**
2. Enter your Access Key ID and Secret Access Key
3. Enter your bucket name and select the region
4. Optionally change the path prefix (default: `wpsigner/`)
5. Click **Test Connection** to verify
6. Click **Save Settings**

---

## How It Works

When all signers complete their signatures, WPsigner uploads the signed PDF to your S3 bucket using **AWS Signature V4** authentication.

| Event | Action |
|-------|--------|
| All signatures complete | PDF uploaded to `s3://bucket/prefix/Title_Date_ID.pdf` |

### Upload Path

The object key is built from the path prefix and a generated file name:

```
{path_prefix}{sanitized_title}_{date}_{document_id}.pdf
```

| Segment | Example | Description |
|---------|---------|-------------|
| `{path_prefix}` | `wpsigner/` | Configurable in settings (default: `wpsigner/`). Trailing slash is enforced automatically. |
| `{sanitized_title}` | `Service-Agreement` | Document title, sanitized for safe file names |
| `{date}` | `2026-03-06` | Signing date in `Y-m-d` format |
| `{document_id}` | `142` | Internal WPsigner document ID |

**Full key example:**

```
wpsigner/Service-Agreement_2026-03-06_142.pdf
```

After a successful upload, WPsigner stores a cloud backup reference in the database with the object key, provider name, and upload timestamp. This metadata powers the download feature in the admin panel.

---

## Use Cases

| Scenario | Configuration |
|----------|---------------|
| Long-term document archival | Use S3 Standard or S3 Glacier for cost-efficient retention |
| Compliance storage with immutability | Enable S3 Object Lock on the bucket |
| Multi-region redundancy | Enable cross-region replication on the bucket |
| Cost-optimized backup | Pair with S3 Lifecycle rules to transition older files to Glacier |
| Multi-cloud strategy | Combine with [Dropbox](/integrations/dropbox/) or [OneDrive](/integrations/onedrive/) |
| Custom folder structure | Use the `wps_s3_backup_key` filter to organize by date, client, or category |

---

## Compatibility

| Component | Supported Versions |
|-----------|--------------------|
| **WPsigner** | 2.1.0+ |
| **WordPress** | 6.0+ |
| **PHP** | 7.4+ |
| **AWS Signature** | V4 |
| **S3 Regions** | All 21 standard AWS regions |
| **S3 Storage Classes** | Standard, Intelligent-Tiering, Glacier (via lifecycle rules) |
| **Max file size** | Limited by PHP `memory_limit` (single PUT upload) |
| **Multisite** | Supported (per-site configuration) |

> **note**
WPsigner's S3 integration uses native AWS Signature V4 signing without the AWS SDK. This keeps the plugin lightweight with zero external dependencies.

---

## Security

| Feature | Details |
|---------|---------|
| **AWS Signature V4** | Requests signed per AWS standard |
| **AES-256-GCM** | Secret key encrypted at rest in database |
| **Minimal Permissions** | Only `PutObject`, `GetObject`, `DeleteObject` needed |
| **Rate Limiting** | Test: 5/min, Save: 10/min per user |
| **Nonce Verification** | All AJAX requests verified |

---

## Troubleshooting

| Issue | Cause | Solution |
|-------|-------|----------|
| `AccessDenied` (403) | IAM policy missing required actions | Verify `s3:PutObject` is granted on the correct bucket ARN |
| `NoSuchBucket` (404) | Bucket name is wrong or bucket was deleted | Double-check the bucket name in WPsigner settings |
| `InvalidAccessKeyId` | Access Key is incorrect or deactivated | Verify the Access Key in IAM Console; create a new one if deactivated |
| `SignatureDoesNotMatch` | Secret key is incorrect or corrupted | Re-enter the Secret Access Key and save |
| `RequestTimeTooSkewed` | Server clock is more than 15 minutes off | Sync your server clock with NTP (`ntpdate pool.ntp.org`) |
| "Security check failed" | Nonce expired | Refresh the page and retry |
| "Too many requests" | Rate limit exceeded | Wait 60 seconds and retry |
| "Failed to sign request" | Credentials not configured or decryption failed | Re-enter both the Access Key and Secret Key, then save |
| Upload succeeds but file not in bucket | Wrong region selected | Ensure the region in WPsigner matches the bucket's actual region |
| Bucket appears empty in console | Path prefix is set | Navigate to the `wpsigner/` folder inside the bucket |
| Large files fail | PHP memory limit too low | Increase `memory_limit` in `php.ini` (recommended: 256M+) |

> **caution**
Never use your AWS root account credentials. Always create a dedicated IAM user with the minimum required permissions for WPsigner.

---

## Developer Hooks

### `wps_s3_backup_key`

Filter the S3 object key before upload. Use this to implement custom naming, folder structures, or routing logic.

```php
add_filter('wps_s3_backup_key', function ($object_key, $document_id, $document, $provider_id) {
    // Only modify for Amazon S3, not Wasabi or R2
    if ($provider_id !== 'amazon_s3') {
        return $object_key;
    }

    // Organize by year/month
    $year  = wp_date('Y');
    $month = wp_date('m');
    return "wpsigner/{$year}/{$month}/" . basename($object_key);
}, 10, 4);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `$object_key` | `string` | Full S3 object key (e.g., `wpsigner/Title_2026-03-06_42.pdf`) |
| `$document_id` | `int` | WPsigner document ID |
| `$document` | `object` | Document object with `title`, `status`, and other properties |
| `$provider_id` | `string` | Provider identifier (`amazon_s3`, `wasabi`, or `cloudflare_r2`) |

> **tip**
The `wps_s3_backup_key` filter is shared across all S3-compatible providers (Amazon S3, Wasabi, Cloudflare R2). Use the `$provider_id` parameter to apply provider-specific logic.

### `wps_s3_uploaded`

Action fired after a successful upload.

```php
add_action('wps_s3_uploaded', function ($document_id, $object_key, $provider_id) {
    if ($provider_id === 'amazon_s3') {
        error_log("Document {$document_id} backed up to S3: {$object_key}");
    }
}, 10, 3);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `$document_id` | `int` | WPsigner document ID |
| `$object_key` | `string` | S3 object key where the file was uploaded |
| `$provider_id` | `string` | Provider identifier |

---

## Next Steps

- [Wasabi](/integrations/wasabi/) — S3-compatible, no egress fees
- [Cloudflare R2](/integrations/cloudflare-r2/) — Zero egress, global CDN
- [Google Drive](/integrations/google-drive/) — OAuth-based backup
- [Dropbox](/integrations/dropbox/) — Dropbox backup
- [OneDrive](/integrations/onedrive/) — Microsoft OneDrive backup

---

# n8n & Make Automation

> Connect WPsigner to n8n, Make (Integromat), and other automation platforms using webhooks.

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/automation/
Markdown: https://docs.wpsigner.com/md/integrations/automation.md

Integrate WPsigner with automation platforms like n8n and Make to create powerful no-code workflows.

## Why Automation Platforms?

| Platform | Best For | Pricing |
|----------|----------|---------|
| **n8n** | Self-hosted, technical users | Free (self-hosted) |
| **Make** | Visual workflows, beginners | Free tier available |
| **Integromat** | Same as Make (rebranded) | Same as Make |

---

## n8n Integration

### Setup n8n Webhook

1. **Add Webhook node** to your n8n workflow
2. **Copy the webhook URL** provided
3. **Configure in WPsigner:**
   - Go to **WPsigner → More → Webhooks**
   - Add new webhook with n8n URL
   - Select events to trigger

### Example: Document Signed → Slack Notification

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  WPsigner   │───▶│    n8n      │───▶│   Slack     │
│  Webhook    │    │  Workflow   │    │   Message   │
└─────────────┘    └─────────────┘    └─────────────┘
```

**n8n Workflow:**

1. **Webhook node** - Receives WPsigner events
2. **IF node** - Check event type = `document.completed`
3. **Slack node** - Send notification

**Webhook payload (JSON):**
```json
{
  "event": "document.completed",
  "document_id": 123,
  "document_title": "Service Agreement",
  "completed_at": "2026-01-15T14:30:00Z",
  "signers": [
    {
      "name": "John Smith",
      "email": "john@example.com",
      "signed_at": "2026-01-15T14:30:00Z"
    }
  ]
}
```

### Example: CRM → Create Document

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  HubSpot    │───▶│    n8n      │───▶│  WPsigner   │
│  New Deal   │    │  Workflow   │    │  Create Doc │
└─────────────┘    └─────────────┘    └─────────────┘
```

**n8n Workflow:**

1. **HubSpot Trigger** - New deal created
2. **HTTP Request node** - Call WPsigner API:
   - Method: POST
   - URL: `https://yoursite.com/wp-json/insigner/v1/documents`
   - Headers:
     - `X-WPS-API-Key`: your_key
     - `X-WPS-API-Secret`: your_secret
   - Body (JSON):
```json
{
  "title": "{{$node.HubSpot.json.dealname}} Agreement",
  "template_id": 123,
  "signers": [{
    "name": "{{$node.HubSpot.json.contact_name}}",
    "email": "{{$node.HubSpot.json.contact_email}}"
  }],
  "send_emails": true
}
```

---

## Make (Integromat) Integration

### Setup Make Webhook

1. **Create new scenario** in Make
2. **Add Webhooks module** → Custom webhook
3. **Copy webhook URL**
4. **Add to WPsigner:**
   - Go to **WPsigner → More → Webhooks**
   - Paste Make webhook URL
   - Select events

### Example: Document Completed → Google Sheets

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  WPsigner   │───▶│    Make     │───▶│Google Sheets│
│  Webhook    │    │  Scenario   │    │   Add Row   │
└─────────────┘    └─────────────┘    └─────────────┘
```

**Make Scenario:**

1. **Webhooks** - Custom webhook (receives WPsigner data)
2. **Router** - Filter by event type
3. **Google Sheets** - Add row with document details

**Data mapping:**
| WPsigner Field | Google Sheets Column |
|----------------|---------------------|
| `document_title` | Document Name |
| `completed_at` | Date Completed |
| `signers[0].name` | Signer Name |
| `signers[0].email` | Signer Email |

### Example: Typeform → WPsigner

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  Typeform   │───▶│    Make     │───▶│  WPsigner   │
│  Response   │    │  Scenario   │    │  Create Doc │
└─────────────┘    └─────────────┘    └─────────────┘
```

**Make Scenario:**

1. **Typeform** - Watch responses
2. **HTTP** module - Make a request:
   - URL: `https://yoursite.com/wp-json/insigner/v1/documents`
   - Method: POST
   - Headers:
     - `X-WPS-API-Key`: your_key
     - `X-WPS-API-Secret`: your_secret
   - Body type: JSON
   - Request content:
```json
{
  "title": "Contract for {{answers.email}}",
  "template_id": 123,
  "signers": [{
    "name": "{{answers.name}}",
    "email": "{{answers.email}}"
  }],
  "send_emails": true
}
```

---

## Common Automation Workflows

### Lead to Contract

```
New CRM lead → Wait 1 hour → Create document → Send for signature
                    ↓
               Lead not qualified? → Skip
```

### Signed Document Storage

```
Document completed → Download PDF → Upload to Google Drive → Update Airtable
```

### Reminder Sequence

```
Document sent → Wait 3 days → Check if signed
                                    ↓
                              Not signed? → Send reminder email
                                    ↓
                              Wait 3 more days → Escalate to sales
```

### Multi-System Sync

```
Document completed → Update CRM → Create invoice → Notify team
```

---

## Webhook Events Reference

| Event | Triggered When |
|-------|---------------|
| `document.created` | New document created |
| `document.sent` | Document emails sent |
| `document.viewed` | Signer opens document |
| `document.signed` | Individual signature applied |
| `document.completed` | All signatures complete |
| `document.declined` | Signer declines |
| `document.expired` | Document expires unsigned |
| `document.voided` | Admin voids document |

---

## Authentication in Automation

### For outgoing calls (to WPsigner)

Include in every HTTP request:
```
Headers:
  X-WPS-API-Key: wps_your_key_here
  X-WPS-API-Secret: your_secret_here
  Content-Type: application/json
```

### For incoming webhooks (from WPsigner)

Verify webhook signature:
```
Header: X-WPS-Signature
Value: HMAC-SHA256 of payload
```

Most automation platforms handle this automatically or allow secret validation.

---

## Tips & Best Practices

### Error Handling

- Add error handling branches in your workflows
- Set up notifications for failed executions
- Log all attempts for debugging

### Rate Limiting

- WPsigner allows 60 requests/minute
- Add delays between bulk operations
- Use batch endpoints when available

### Testing

1. Use test webhooks first
2. Verify data mapping works
3. Test error scenarios
4. Monitor first few production runs

---

## Troubleshooting

| Issue | Solution |
|-------|----------|
| Webhook not triggering | Verify URL is correct and accessible |
| Authentication errors | Check API key and secret |
| Missing data | Verify event includes needed fields |
| Timeout errors | Increase timeout, check WPsigner server |

---

## Next Steps

- [Webhooks Reference](/api/webhooks/)
- [REST API](/api/)
- [WPsigner for Uncanny Automator](/addons/uncanny-automator/) — stay inside WordPress instead of Zapier when both plugins are on the same site

---

# Cloudflare R2

> Zero egress fee storage with global CDN

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/cloudflare-r2/
Markdown: https://docs.wpsigner.com/md/integrations/cloudflare-r2.md

Back up signed PDFs to **Cloudflare R2** — S3-compatible storage with zero egress fees and automatic global distribution.

---

## Requirements

- WPsigner **2.1.0+**
- A [Cloudflare](https://dash.cloudflare.com/) account
- R2 enabled on your account
- API token with R2 read/write permissions

---

## How It Works

When a signer completes a document, WPsigner automatically uploads the final PDF to your R2 bucket. The process follows the same S3-compatible flow used by all cloud storage integrations:

1. **Document signed** — All required signers complete the document.
2. **PDF generated** — WPsigner creates the final certified PDF.
3. **Upload triggered** — The plugin sends the PDF to your R2 bucket using the S3 `PutObject` operation with AWS Signature V4 authentication against the R2 endpoint.
4. **Confirmation stored** — WPsigner records the remote URL and upload status in the document metadata.

> **note**
R2 does not require you to select a region. Cloudflare automatically distributes stored objects across its global network for low-latency access.

---

## Setup

### Step 1: Create an R2 Bucket

1. Go to your [Cloudflare Dashboard](https://dash.cloudflare.com/) and select your account.
2. Navigate to **R2** in the left sidebar.
3. Click **Create bucket**.
4. Enter a bucket name (lowercase, no spaces). Example: `wpsigner-signed-docs`.
5. Click **Create bucket**.

> **caution**
Bucket names must be unique within your Cloudflare account and follow DNS naming conventions (3-63 characters, lowercase letters, numbers, and hyphens only).

### Step 2: Create API Tokens

1. In the Cloudflare Dashboard, go to **R2 → Manage R2 API Tokens**.
2. Click **Create API token**.
3. Set permissions to **Object Read & Write**.
4. Under **Specify bucket(s)**, select the bucket you created or choose **Apply to all buckets** if you plan to use multiple buckets.
5. Click **Create API Token**.
6. Copy the **Access Key ID** and **Secret Access Key** immediately — the secret is shown only once.

> **important**
Store these credentials in a secure location. If you lose the secret access key, you must revoke the token and create a new one.

### Step 3: Find Your Account ID

Your Cloudflare Account ID appears in the dashboard URL:

```
https://dash.cloudflare.com/{account-id}
```

It is a 32-character hexadecimal string. You can also find it on the **R2 overview** page under **Account ID**.

### Step 4: Configure WPsigner

1. Go to **WPsigner → Integrations → Cloudflare R2** in your WordPress admin.
2. Enter your **Account ID** (32-character hex string from Step 3).
3. Enter the **Access Key ID** and **Secret Access Key** from Step 2.
4. Enter the **Bucket Name** exactly as created in Step 1.
5. Click **Test Connection** to verify credentials and bucket access.
6. Once the test passes, click **Save Settings**.

> **tip**
There is no region selector for R2. WPsigner constructs the endpoint automatically from your Account ID: `https://{account-id}.r2.cloudflarestorage.com`.

---

## Use Cases

| Use Case | Description |
|----------|-------------|
| **Zero-cost downloads** | Retrieve signed documents as often as needed without paying egress fees — ideal for client portals or frequent access. |
| **Global distribution** | Cloudflare's CDN serves files from the nearest edge location, reducing download latency for international teams. |
| **Regulatory compliance** | Maintain off-server copies of signed documents for audit trails and retention policies. |
| **Disaster recovery** | Keep an independent backup of all signed PDFs in case of WordPress server failure. |
| **Cost-effective archival** | At $0.015/GB/month with a 10 GB free tier, R2 is well suited for small-to-medium document volumes. |

---

## R2 vs S3 vs Wasabi

| Feature | Cloudflare R2 | Amazon S3 | Wasabi |
|---------|--------------|-----------|--------|
| **Egress Fees** | $0 | $0.09/GB | $0 |
| **Storage** | $0.015/GB/mo | $0.023/GB/mo | $6.99/TB/mo |
| **Free Tier** | 10 GB | 5 GB (12mo) | None |
| **Global CDN** | Built-in | Extra (CloudFront) | No |

---

## Security

Same security as Amazon S3: AWS Signature V4, AES-256-GCM encryption, rate limiting, nonce verification. Account ID is validated as 32-character hex.

---

## Troubleshooting

| Problem | Cause | Solution |
|---------|-------|----------|
| **Test connection fails with "InvalidAccessKeyId"** | The access key ID is incorrect or the API token has been revoked. | Verify the token exists under **R2 → Manage R2 API Tokens**. Create a new token if needed. |
| **Test connection fails with "SignatureDoesNotMatch"** | The secret access key is incorrect or contains trailing whitespace. | Re-paste the secret key carefully, ensuring no extra spaces. |
| **"NoSuchBucket" error** | The bucket name in WPsigner does not match an existing R2 bucket. | Check the exact bucket name in the Cloudflare Dashboard and re-enter it. |
| **"InvalidAccountId" or endpoint error** | The Account ID is malformed or does not match a valid Cloudflare account. | Confirm the 32-character hex Account ID from your dashboard URL or the R2 overview page. |
| **Uploads succeed but files are empty (0 bytes)** | Server memory or execution time limits are too low to process large PDFs. | Increase `memory_limit` to at least `256M` and `max_execution_time` to `120` in your `php.ini`. |
| **Intermittent timeout errors** | Network instability between your server and the Cloudflare endpoint. | Check your server's outbound connectivity. R2 automatically routes to the nearest Cloudflare data center, so the issue is typically on the origin server side. |

---

## Compatibility

| Component | Minimum Version | Recommended |
|-----------|----------------|-------------|
| **WPsigner** | 2.1.0 | Latest |
| **WordPress** | 5.8 | 6.4+ |
| **PHP** | 7.4 | 8.1+ |
| **cURL extension** | Required | Latest |
| **OpenSSL extension** | Required | Latest |

---

## Next Steps

- [Amazon S3](/integrations/amazon-s3/) — Native AWS storage with the broadest region coverage.
- [Wasabi](/integrations/wasabi/) — S3-compatible hot storage with no egress fees.
- [Google Drive](/integrations/google-drive/) — Sync signed documents to your Google Workspace.
- [Dropbox](/integrations/dropbox/) — Automatic backups to your Dropbox account.

---

# 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

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

---

# Didit.me — Identity Verification (KYC)

> Integrate Didit.me identity verification into WPsigner to require signers to verify their identity with a government-issued ID, selfie, and liveness detection before signing.

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/didit/
Markdown: https://docs.wpsigner.com/md/integrations/didit.md

**Didit.me** is an AI-powered Know Your Customer (KYC) identity verification platform. When integrated with WPsigner, signers must prove their identity using a government-issued ID document, a selfie, and a real-time liveness check before they can sign a document.

Identity verification strengthens the evidence associated with a signing event, but it does not by itself guarantee legal validity or replace advice about the requirements in your jurisdiction.

  
    Compares the selfie with the ID photo using biometric analysis with a confidence score.
  
  
    Prevents spoofing attacks with real-time liveness checks — no photos or videos accepted.
  
  
    Extracts and validates data from passports, national IDs, and driver's licenses from 190+ countries.
  
  
    Verification decisions and relevant metadata are recorded in the WPsigner Activity Timeline; identity images remain with Didit.
  

---

## How It Works

The integration follows a **two-step activation model**:

1. **Configure credentials** in **WPsigner → Integrations → Didit**.
2. **Choose the KYC policy** in **WPsigner → More → Security & Compliance**.

Once enabled, this is the end-to-end flow:

```
Signer opens signing page
        ↓
WPsigner detects KYC is required
        ↓
Signer clicks "Verify Identity"
        ↓
WPsigner calls Didit API → creates a verification session
        ↓
Signer is redirected to Didit's verification page (new tab)
        ↓
Signer completes: ID scan → Selfie → Liveness check
        ↓
Didit sends webhook to WPsigner with result (Approved / Declined)
        ↓
Callback page closes → signing page unlocks automatically
        ↓
Verified signer can now sign the document
```

The signing page polls for status in the background using `BroadcastChannel` and `localStorage` as cross-tab communication mechanisms, meaning the signer never has to manually reload.

---

## Prerequisites

Before setting up the integration you need:

- A **Didit Business account** at [business.didit.me](https://business.didit.me)
- At least one **Verification Workflow** created in the Didit console
- An **API Key** with sufficient permissions
- A **Webhook Secret** from your workflow settings

---

## Setup Guide

1. **Create a Didit Business account**

   Go to [business.didit.me](https://business.didit.me) and register. No credit card is required for the free tier.

2. **Create a Verification Workflow**

   Inside the Didit Business Console:
   - Go to **Workflows** → **Create Workflow**
   - Add the following steps (in order):
     - **ID Document Verification** — scans and validates government-issued ID
     - **Selfie Capture** — takes a photo of the signer
     - **Liveness Detection** — confirms the person is physically present
   - Save and note the **Workflow ID** (a UUID like `550e8400-e29b-41d4-a716-446655440000`)

3. **Get your API Key**

   - Go to **Settings → API Keys** in the Didit Business Console
   - Click **Create API Key**
   - Copy the key immediately — it is only shown once

4. **Get your Webhook Secret**

   - Open your Workflow settings
   - Go to the **Webhook** tab
   - Copy the **Webhook Signing Secret** (used for HMAC-SHA256 signature verification)

5. **Configure WPsigner**

   In your WordPress admin, go to **WPsigner → Integrations → Didit**:
   - Paste your **API Key**
   - Paste your **Workflow ID**
   - Paste your **Webhook Secret**
   - Click **Save Settings**

6. **Register the Webhook URL**

   - Copy the **Webhook URL** shown in the WPsigner Didit settings page
   - It follows this format: `https://yoursite.com/?wps_didit_webhook=1`
   - Go back to Didit Business Console → your Workflow → **Webhook** tab
   - Paste the URL and save

7. **Test the connection**

   Click **Test Connection** in the WPsigner Didit settings page. A success message confirms your API key is valid. The test sends a request to the Didit API and expects a `404` response (a valid key will get a 404 for a non-existent dummy session — that confirms authentication worked).

8. **Enable KYC requirement**

   Go to **WPsigner → More → Security & Compliance** and set the KYC policy to **Always**, **Per document**, or **Off**. With **Per document**, enable KYC in the document's Review step only when it is needed.

---

## Credentials Reference

| Field | Where to Find It | Description |
|-------|-----------------|-------------|
| **API Key** | Business Console → Settings → API Keys | Server-to-server authentication key |
| **Workflow ID** | Business Console → Workflows → (your workflow) | UUID that identifies which verification steps to run |
| **Webhook Secret** | Business Console → Workflows → Webhook tab | HMAC-SHA256 signing secret for webhook verification |
| **Webhook URL** | WPsigner → Integrations → Didit (read-only) | URL Didit will POST results to — register this in your workflow |

All credentials are encrypted at rest and are never stored in plaintext in the database.

---

## Signer Experience

From the signer's perspective, the verification flow is seamless:

1. The signer opens the signing link (`/wpsigner/{token}`)
2. If KYC is required and not yet verified, a verification prompt appears — **they cannot proceed to sign without completing it**
3. Clicking **"Verify My Identity"** creates a session and opens Didit's verification page in a **new tab** (popup)
4. The signer completes three steps within the Didit interface:
   - Scans or photographs their ID document (passport, national ID, or driver's license)
   - Takes a live selfie
   - Completes a liveness challenge (e.g., blink, turn head)
5. Upon completion, the Didit tab closes automatically and the signing page detects the result via `BroadcastChannel` / `localStorage`
6. If approved, the signing interface unlocks and the signer can proceed
7. If declined, an error message is shown and the signer can retry

The entire process typically takes **30–90 seconds**.

---

## Verification Statuses

WPsigner tracks the following KYC statuses per signer:

| Status | Meaning |
|--------|---------|
| `pending` | A session has been created; the signer has not yet completed verification |
| `Started` | The signer has opened the Didit verification page |
| `Callback_Received` | The signer returned from Didit (browser callback received); awaiting authoritative webhook |
| `Approved` | Identity verified — signer can proceed to sign |
| `Declined` | Verification failed — signer cannot sign |
| `Expired` | Session expired before completion (a new session will be created on next attempt) |

**Only `Approved` is authoritative.** The browser callback (`Callback_Received`) is an optimistic UI signal only — the final decision always comes from Didit's server-side webhook. The signing page is only fully unlocked once the webhook confirms `Approved`.

---

## Security Architecture

The integration is designed with a fail-closed security model — when in doubt, it blocks rather than allows.

### Webhook Signature Verification (X-Signature-V2)

Every webhook from Didit includes an `X-Signature-V2` header and an `X-Timestamp` header. WPsigner verifies the webhook before processing:

1. **Timestamp freshness check** — Rejects webhooks older than ±5 minutes to prevent replay attacks
2. **HMAC-SHA256 verification** — Computes `HMAC-SHA256(sorted_json_body, webhook_secret)` and compares with `hash_equals()` (timing-safe comparison)
3. **Fail-closed** — If no webhook secret is configured, all webhooks are rejected with `401`

The signature algorithm:
- Sorts all JSON keys recursively
- Converts integer-value floats to integers
- Encodes with `JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE`
- Signs with HMAC-SHA256

### Status Immutability

Once a signer's KYC status reaches `Approved`, it **cannot be downgraded** by any subsequent webhook. This prevents an attacker from forging a `Declined` webhook after approval.

### Idempotency & Replay Prevention

Each webhook is deduplicated using a transient key based on `MD5(session_id + status + timestamp)` with a 60-second TTL. Duplicate or replayed webhooks are silently ignored with `200 OK` (to prevent Didit from retrying).

### Token Ownership Verification (IDOR Prevention)

When a signer requests a KYC session via AJAX, WPsigner verifies that the submitted `token` matches the signer's `access_token` using `hash_equals()`. This prevents Insecure Direct Object Reference (IDOR) attacks — a signer cannot create or check sessions for other signers.

### Rate Limiting

| Operation | Limit |
|-----------|-------|
| Create session | 50 attempts per signer per hour |
| Check status (poll) | 60 requests per signer per minute |
| Callback URL | 10 requests per signer per minute |
| Save settings (admin) | 10 saves per user per minute |

### Session Reuse

If a signer already has an active (non-terminal) session, WPsigner reuses the existing Didit verification URL instead of creating a new session. This prevents unnecessary API charges and avoids flooding Didit with duplicate sessions.

Terminal statuses that force a new session: `Declined`, `Expired`, `Failed`, `Abandoned`.

### Credential Storage

Credentials are stored encrypted in WordPress options:

| Option | Content |
|--------|---------|
| `wps_didit_api_key` | AES-256 encrypted API key |
| `wps_didit_workflow_id` | Plain text Workflow UUID |
| `wps_didit_webhook_secret` | AES-256 encrypted webhook secret |

---

## Audit Trail Integration

Every KYC event is automatically logged to the WPsigner Activity Timeline with full detail:

| Event | When it fires | Logged Detail |
|-------|--------------|---------------|
| `kyc_session_started` | When a new verification session is created | Session UUID |
| `kyc_callback_received` | When the user returns from Didit's page | "Awaiting webhook confirmation" note |
| `kyc_verified` | When webhook confirms `Approved` | Document type, issuing country, face match score, liveness result |
| `kyc_declined` | When webhook confirms `Declined` | Provider and session info |

The biometric evidence stored on `Approved` includes:

- `face_match_score` — percentage confidence (e.g., `98.2`)
- `document_type` — e.g., `PASSPORT`, `NATIONAL_ID`, `DRIVER_LICENSE`
- `issuing_country` — ISO 3166-1 alpha-2 country code
- `liveness` — liveness check result
- `provider` — `Didit.me`
- `method` — `ID + Selfie + Liveness`

This data is stored as JSON in the `kyc_data` column and is included in the legal PDF certificate generated at signing completion.

---

## API Reference

WPsigner communicates with the **Didit API v3** at `https://verification.didit.me/v3`.

### Create Session

**Endpoint:** `POST /session/`

Creates a new verification session for a signer.

**Request body sent by WPsigner:**

```json
{
  "workflow_id": "550e8400-e29b-41d4-a716-446655440000",
  "vendor_data": "signer-42",
  "callback": "https://yoursite.com/?wps_didit_callback=1&signer_id=42",
  "callback_method": "both",
  "metadata": "{\"document_id\": 10, \"signer_id\": 42, \"site_url\": \"https://yoursite.com\"}",
  "language": "en",
  "contact_details": {
    "email": "signer@example.com",
    "send_notification_emails": false
  },
  "expected_details": {
    "first_name": "John",
    "last_name": "Doe"
  }
}
```

**Response** (on success):

```json
{
  "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "url": "https://verify.didit.me/session/a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```

WPsigner stores `session_id` and `url` in the `wps_signers` row for that signer.

### Retrieve Session Decision

**Endpoint:** `GET /session/{session_id}/decision/`

Retrieves the authoritative decision for a completed session. Called by WPsigner:
- **Always** when an `Approved` webhook is received (to fetch biometric details)
- **Proactively** when the signer returns from Didit (`Callback_Received` status) to get the decision faster than waiting for the webhook

**Response** (on approval):

```json
{
  "session_id": "a1b2c3d4-...",
  "status": "Approved",
  "features": [
    { "feature": "face_match", "score": 0.982 },
    { "feature": "liveness", "status": "passed" }
  ],
  "id_verifications": [
    {
      "document_type": "PASSPORT",
      "issuing_country": "US"
    }
  ]
}
```

### Authentication

All API requests include:

```http
x-api-key: YOUR_API_KEY
Content-Type: application/json
Accept: application/json
User-Agent: WPsigner/3.x.x
```

---

## Webhook Reference

Didit sends `POST` requests to your configured webhook URL when a session status changes.

### Webhook URL

Your webhook URL (shown in WPsigner settings) follows this format:

```
https://yoursite.com/?wps_didit_webhook=1
```

Make sure your site is publicly accessible from the internet. If you are running WordPress on localhost or behind a firewall, Didit cannot reach your webhook URL. Use a tunneling service like [ngrok](https://ngrok.com) for local testing.

### Webhook Payload

```json
{
  "session_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "Approved",
  "vendor_data": "signer-42",
  "metadata": "{\"document_id\": 10, \"signer_id\": 42, \"site_url\": \"https://yoursite.com\"}",
  "features": [
    { "feature": "face_match", "score": 0.982 },
    { "feature": "liveness", "status": "passed" }
  ],
  "id_verifications": [
    {
      "document_type": "PASSPORT",
      "issuing_country": "US"
    }
  ]
}
```

### Webhook Headers

| Header | Description |
|--------|-------------|
| `X-Signature-V2` | HMAC-SHA256 signature of the request body |
| `X-Timestamp` | Unix timestamp of when the request was sent |
| `Content-Type` | `application/json` |

### Allowed Status Values

WPsigner only accepts the following status values from Didit webhooks. Unknown statuses are silently ignored:

- `Approved`
- `Declined`
- `Pending`
- `Started`
- `Expired`

### Webhook Response

WPsigner always responds with `200 OK` and a JSON body. Didit will retry on non-200 responses, so returning 200 even for invalid payloads (after logging) is intentional.

---

## Database Schema

The Didit integration adds four columns to the `{prefix}_wps_signers` table via a one-time migration:

| Column | Type | Description |
|--------|------|-------------|
| `kyc_session_id` | `VARCHAR(100)` | Didit session UUID |
| `kyc_status` | `VARCHAR(50)` | Current KYC status (see status table above) |
| `kyc_verified_at` | `DATETIME` | Timestamp when `Approved` was received |
| `kyc_data` | `LONGTEXT` | JSON blob with biometric evidence and session metadata |

The migration runs automatically on plugin initialization via `WPS_Didit::ensure_tables()`.

---

## Troubleshooting

### "API request failed" on Test Connection

- Double-check the API key has been copied correctly (no trailing spaces)
- Make sure the key has not been revoked in the Didit console
- Test Connection returns `401` or `403` → API key is invalid
- Test Connection returns `404` → API key is valid (expected for dummy UUID)

### Webhook not being received

- Verify the webhook URL is registered in Didit's Business Console under your workflow
- Make sure your site is publicly reachable (not localhost or behind a firewall)
- Check your WordPress permalink settings — **pretty permalinks must be enabled**
- Check for a web application firewall (WAF) blocking POST requests from Didit's IPs
- Enable `WP_DEBUG` to see error logs: `define('WP_DEBUG', true); define('WP_DEBUG_LOG', true);`

### "Webhook secret not configured" error

The webhook secret is **mandatory**. If you see this error:
1. Go to **WPsigner → Integrations → Didit**
2. Paste your Webhook Secret from the Didit Business Console
3. Click **Save Settings**

### Signer status stuck on "pending"

This usually means the webhook was not received. Check:
1. Is the webhook URL registered in Didit?
2. Did Didit receive a non-200 response? Check the Didit Business Console → Webhook logs
3. Is the webhook secret correct?

WPsigner also queries the Didit API directly when the signer returns to the signing page (`Callback_Received` status), so even if the webhook is delayed, the status should update within a few seconds of the signer completing verification.

### "Invalid signature" webhook rejection

This means the webhook secret saved in WPsigner does not match the one in Didit. Resave the correct secret in **WPsigner → Integrations → Didit**.

### Didit KYC toggle reverts to OFF after saving

Ensure that you are saving the policy from **WPsigner → More → Security & Compliance**, not the Didit integration settings page. The credentials and the KYC policy are configured separately.

---

## Frequently Asked Questions

**Which countries and document types are supported?**

Didit supports government-issued IDs from 190+ countries, including passports, national identity cards, and driver's licenses.

**Does WPsigner store biometric data (photos, scans)?**

No. WPsigner **never** receives or stores the actual ID photos or selfie images. Only the verification decision metadata (face match score, document type, country, liveness result) is stored for the audit trail.

**Can signers retry if their verification is declined?**

Yes. A new verification session is automatically created when the signer clicks "Verify My Identity" again after a `Declined` or `Expired` status.

**Can I use Didit.me on a per-document basis instead of globally?**

Yes. Set the KYC policy to **Per document** in **Security & Compliance**, then enable KYC in the document's Review step. **Always** requires KYC for every signing request, and **Off** disables the gate.

**What happens if the Didit service is down?**

If the Didit API is unreachable when creating a session, the signer sees an error message and cannot proceed. WPsigner uses a 15-second timeout for API requests. If the webhook does not arrive but the signer completes verification, WPsigner will query the Didit API directly on the next status poll to retrieve the decision.

**Is Didit.me GDPR compliant?**

Yes. Didit.me processes verification data under its own privacy policy. WPsigner only stores the non-PII verification result metadata described above. Refer to [Didit's Privacy Policy](https://didit.me/privacy) for their data processing terms.

**What does "face match score" mean?**

The face match score (0–100%) represents the biometric confidence that the selfie matches the ID document photo. A score ≥ 70% is considered a passing match by WPsigner's audit trail logic.

---

## Related Documentation

- [Security & Compliance](/core-features/security-compliance/)
- [Audit Trails](/digital-identity/audit-trails/)
- [Legal Compliance](/compliance/)
- [Didit.me Official Documentation](https://docs.didit.me/)

---

# Dropbox

> Back up signed PDFs to your Dropbox account

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/dropbox/
Markdown: https://docs.wpsigner.com/md/integrations/dropbox.md

Automatically upload signed PDFs to **Dropbox** when all signatures are complete.

---

## Requirements

- WPsigner **2.1.0+**
- A [Dropbox](https://www.dropbox.com/) account
- A Dropbox App (created in the App Console)

---

## Setup

### Step 1: Create a Dropbox App

1. Go to the [Dropbox App Console](https://www.dropbox.com/developers/apps)
2. Click **Create App**
3. Choose **Scoped access** → **Full Dropbox**
4. Name it (e.g., "WPsigner Backup")
5. Under **Permissions**, enable `files.content.write`
6. Copy the **App Key** and **App Secret**

### Step 2: Set Redirect URI

In the Dropbox App Console, add the following redirect URI:

```
https://your-site.com/wp-admin/admin.php?page=wpsigner-dropbox
```

### Step 3: Configure WPsigner

1. Go to **WPsigner → Integrations → Dropbox**
2. Enter your App Key and App Secret
3. Click **Save Settings**
4. Click **Authorize Dropbox** to connect your account
5. Approve the permissions in Dropbox
6. You'll be redirected back showing **Connected**

---

## How It Works

When all signers complete their signatures, WPsigner triggers the `wps_pdf_completed` hook. The Dropbox integration then:

1. Generates the signed PDF file locally
2. Builds the destination path using the configured folder and naming convention
3. Uploads the file via **Dropbox API v2** (`/2/files/upload`)
4. Creates the destination folder automatically if it doesn't exist
5. Logs the upload to the WPsigner audit trail

| Event | Action |
|-------|--------|
| All signatures complete | PDF uploaded to `Dropbox:/WPsigner/Title_Date_ID.pdf` |

> **note**
If a file with the same name already exists, Dropbox autorenames it (e.g., `Contract_2026-03-06_42 (1).pdf`) to prevent overwriting.

---

## File Naming

The default file naming pattern is:

```
{folder}/{sanitized_title}_{date}_{document_id}.pdf
```

| Segment | Example | Description |
|---------|---------|-------------|
| `{folder}` | `/WPsigner` | Configurable in settings (default: `/WPsigner`) |
| `{sanitized_title}` | `Service-Agreement` | Document title, sanitized for safe file names |
| `{date}` | `2026-03-06` | Signing date in `Y-m-d` format |
| `{document_id}` | `142` | Internal WPsigner document ID |

**Full path example:**

```
/WPsigner/Service-Agreement_2026-03-06_142.pdf
```

You can change the destination folder in **WPsigner → Integrations → Dropbox**. The folder path must start with `/`.

---

## Use Cases

| Scenario | Configuration |
|----------|---------------|
| Centralized contract archive | Default folder `/WPsigner`, all documents in one location |
| Per-client organization | Use the `wps_dropbox_backup_path` filter to route files by client |
| Compliance backup | Pair with [Amazon S3](/integrations/amazon-s3/) for redundant storage |
| Team collaboration | Set folder to a shared Dropbox folder accessible by your team |
| Automated workflows | Combine with Dropbox Automations to trigger post-upload actions |

---

## Compatibility

| Component | Supported Versions |
|-----------|--------------------|
| **WPsigner** | 2.1.0+ |
| **WordPress** | 6.0+ |
| **PHP** | 7.4+ |
| **Dropbox API** | v2 |
| **Dropbox Plans** | All plans (Basic, Plus, Professional, Business) |
| **Max file size** | Limited by PHP `memory_limit` (single upload, no chunking) |
| **Multisite** | Supported (per-site configuration) |

---

## Security

| Feature | Details |
|---------|---------|
| **OAuth 2.0** | Standard authorization flow with offline tokens |
| **AES-256-GCM** | App secret and tokens encrypted at rest |
| **Token Refresh** | Automatic — access tokens renewed when expired |
| **Rate Limiting** | Test: 5/min, Save: 10/min per user |

---

## Troubleshooting

| Issue | Cause | Solution |
|-------|-------|----------|
| "Not connected. Please authorize first." | OAuth tokens are missing or cleared | Click **Authorize Dropbox** again |
| "Security check failed" | Nonce expired | Refresh the page and retry |
| "Too many requests" | Rate limit exceeded | Wait 60 seconds and retry |
| Upload test succeeds but PDFs don't appear | Integration is not enabled | Toggle the **Enabled** switch and save |
| "Upload failed" with `path/not_found` | Folder path is invalid | Ensure folder starts with `/` (e.g., `/WPsigner`) |
| OAuth redirect shows error | Redirect URI mismatch | Verify the redirect URI in Dropbox App Console matches exactly |
| Tokens expire unexpectedly | App secret was changed in Dropbox Console | Re-authorize after updating the app secret in WPsigner |
| Files overwritten | Different documents share the same title and date | Document ID in the filename prevents this; check for ID collisions |
| Large files fail to upload | PHP memory limit too low | Increase `memory_limit` in `php.ini` (recommended: 256M+) |

> **caution**
If you change the App Key or App Secret in the Dropbox Console, you must update them in WPsigner and re-authorize the connection. Existing tokens become invalid.

---

## Developer Hooks

### `wps_dropbox_backup_path`

Filter the full Dropbox path before upload. Use this to customize folder structure, naming conventions, or route files dynamically.

```php
add_filter('wps_dropbox_backup_path', function ($dropbox_path, $document_id, $document) {
    // Organize by year/month
    $year  = wp_date('Y');
    $month = wp_date('m');
    return "/WPsigner/{$year}/{$month}/" . basename($dropbox_path);
}, 10, 3);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `$dropbox_path` | `string` | Full Dropbox path (e.g., `/WPsigner/Title_2026-03-06_42.pdf`) |
| `$document_id` | `int` | WPsigner document ID |
| `$document` | `object` | Document object with `title`, `status`, and other properties |

### `wps_dropbox_uploaded`

Action fired after a successful upload. Use this for post-upload processing like notifications or logging.

```php
add_action('wps_dropbox_uploaded', function ($document_id, $dropbox_path) {
    error_log("Document {$document_id} backed up to Dropbox: {$dropbox_path}");
}, 10, 2);
```

---

## Next Steps

- [OneDrive](/integrations/onedrive/) — Microsoft OneDrive backup
- [Google Drive](/integrations/google-drive/) — Google Drive backup
- [Amazon S3](/integrations/amazon-s3/) — S3 bucket storage
- [Cloudflare R2](/integrations/cloudflare-r2/) — Zero egress, global CDN
- [Wasabi](/integrations/wasabi/) — S3-compatible, no egress fees

---

# Elementor Forms

> Automatically generate signing documents from Elementor Pro form submissions.

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/elementor-forms/
Markdown: https://docs.wpsigner.com/md/integrations/elementor-forms.md

## Overview

The **Elementor Forms** integration lets you automatically create WPsigner documents whenever a user submits an Elementor Pro form. Each submission can generate a pre-filled document and optionally send it for signing immediately.

> **note**
This integration requires **Elementor Pro** (paid). The free version of Elementor does not include the Forms widget.

## Requirements

| Requirement | Version |
|---|---|
| WPsigner | 1.5.0+ |
| Elementor Pro | 3.0+ |
| WordPress | 5.8+ |
| PHP | 7.4+ |

## Setup

### 1. Navigate to Integration Settings

Go to **WPsigner → Integrations** and click **Configure** on the Elementor Forms card.

### 2. Create a Feed

Click **New Feed** to open the feed editor:

| Field | Description |
|---|---|
| **Feed Name** | A descriptive name (e.g. "Contact NDA") |
| **Elementor Form** | Select from discovered forms on your site |
| **WPsigner Template** | The template to use for document generation |
| **Document Title** | Supports `{{signer_name}}`, `{{signer_email}}`, `{{date}}` |
| **Primary Signer** | Map Name and Email fields from the form |
| **Variable Mapping** | Map form fields to template variables |
| **Auto-send** | Send signing email immediately after creation |

### 3. Map Fields

After selecting a form, its fields will load automatically. Map the **Name** and **Email** fields to identify the signer.

Use **Variable Mapping** to fill template variables with form data. For example:
- `custom.company` → Company field
- `custom.phone` → Phone field

### 4. Enable and Save

Toggle "Feed enabled" and click **Save Feed**. The feed will process all future submissions of the selected form.

## How It Works

```
User submits Elementor form
        ↓
WPsigner checks for matching feeds (by form_id)
        ↓
Creates document from template with mapped fields
        ↓
(Optional) Sends signing email to signer
```

1. When a form is submitted, the `elementor_pro/forms/new_record` hook fires.
2. WPsigner checks if any enabled feeds match the submitted form's ID.
3. For each matching feed, it extracts signer name/email from the configured fields.
4. A new document is created from the selected template, with form fields mapped to template variables.
5. If auto-send is enabled, a signing request email is dispatched.

## Form Discovery

WPsigner automatically scans all **published** pages and posts that use Elementor to discover forms. Each form is identified by its widget ID and the page/post title where it appears.

## Security

| Feature | Implementation |
|---|---|
| Authentication | Nonce verification + `manage_options` capability on every AJAX handler |
| Input Validation | `sanitize_text_field`, `absint`, regex for variable names |
| Email Validation | `sanitize_email` + `is_email` double check |
| Rate Limiting | 10 documents/minute per form via WordPress transients |
| Duplicate Prevention | SHA-256 submission hash stored in transients for 1 hour |
| Error Logging | PII-redacted logging when `WP_DEBUG` is enabled |

## Multiple Feeds

You can create multiple feeds for the same form — for example, to generate different documents for different templates. Each feed operates independently with its own field mappings and auto-send settings.

## Troubleshooting

### No forms appear in the dropdown
- Ensure Elementor Pro is installed and activated.
- Check that you have at least one published page with an Elementor Form widget.
- Forms on draft or private pages are not discovered.

### Document not created on submission
- Verify the feed is **enabled**.
- Check that the form ID matches (edit the feed to confirm).
- Look in **WPsigner → Audit** for error logs.
- Ensure the template still exists (deleted templates cause feed failures).

### Signer doesn't receive email
- Confirm **Auto-send** is checked in the feed settings.
- Verify the mapped email field contains a valid email address.
- Check your WordPress email configuration (SMTP plugin, mail logs).

## Technical Reference

### Hooks Used

| Hook | Purpose |
|---|---|
| `elementor_pro/forms/new_record` | Processes form submissions |

### Option Keys

| Option | Description |
|---|---|
| `wps_elementorforms_feeds` | Stores all feed configurations (JSON) |

### AJAX Actions

| Action | Purpose |
|---|---|
| `wps_ef_get_form_fields` | Retrieve fields for a specific form |
| `wps_ef_save_feed` | Save a feed configuration |
| `wps_ef_get_feed` | Get a single feed by ID |
| `wps_ef_delete_feed` | Delete a feed |
| `wps_ef_toggle_feed` | Enable/disable a feed |

---

## Use Cases

| Scenario | Setup |
|----------|-------|
| **Landing page NDA** | Elementor landing page form → NDA template → Auto-send |
| **Service quote** | Quote request form → Service agreement template |
| **Event registration** | Registration form → Waiver template → Auto-send |
| **Client onboarding** | Onboarding form → Client contract template → Auto-send |
| **Freelancer proposals** | Project brief form → Proposal/contract template |
| **Rental agreements** | Booking form → Rental agreement template → Auto-send |
| **Employee agreements** | HR form → Employment contract template |
| **Consent collection** | Consent form → Consent document template → Auto-send |

> **tip**
Because Elementor forms live inside page designs, you can create dedicated landing pages with matching branding. Pair a styled Elementor form with a WPsigner template for a seamless brand experience from form to signature.

---

## Compatibility

| Component | Supported Versions |
|-----------|-------------------|
| **Elementor Pro** | 3.0+ (required for Forms widget) |
| **WPsigner** | 1.5.0+ |
| **WordPress** | 5.8+ |
| **PHP** | 7.4+ |

> **caution**
The free version of Elementor does **not** include the Forms widget. You need **Elementor Pro** to use this integration. Verify your license at [elementor.com](https://elementor.com/).

---

## Next Steps

- [Templates](/core-features/form-fields/) — Learn more about creating and managing templates
- [Document Workflow](/core-features/creating-documents/) — Understanding document statuses and actions
- [REST API](/api/) — For advanced programmatic integrations
- [WhatsApp Integration](/integrations/whatsapp/) — Send signing links via WhatsApp
- [Gravity Forms](/integrations/gravity-forms/) — Alternative form plugin integration
- [Fluent Forms](/integrations/fluent-forms/) — Fluent Forms integration
- [Automation](/integrations/automation/) — Connect with workflow automation platforms

---

# Fluent Community

> Let logged-in Fluent Community members open, sign, and download WPsigner documents from their profile or community header.

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/fluent-community/
Markdown: https://docs.wpsigner.com/md/integrations/fluent-community.md

The Fluent Community integration adds a **Documents to sign** link to each member's own profile and, optionally, the community header. The link opens WPsigner's signer portal and shows documents matched to the member's WordPress account email.

## Requirements

- WPsigner **3.1.2+**
- WordPress **5.8+**
- PHP **7.4+**
- Fluent Community installed and active on the same WordPress site
- Pretty permalinks (any structure except **Plain**) recommended

No external API key is required. The integration uses local WordPress and Fluent Community hooks.

## Configure the integration

1. Install and activate Fluent Community.
2. Go to **WPsigner → More → Integrations**.
3. Find **Fluent Community** under **WordPress Plugins**.
4. Enable the module if its card shows **Disabled**.
5. Click **Configure**.
6. Click **Test Connection** to confirm Fluent Community is detected.
7. Enable **Fluent Community Integration**.
8. Choose where the link should appear, then click **Save Settings**.

## Settings

| Setting | Default | Purpose |
|---------|---------|---------|
| **Enable Fluent Community Integration** | Off | Activates profile/header links and the documents endpoint |
| **Custom name** | `Documents to sign` | Changes the member-facing link label; maximum 80 characters |
| **Profile actions** | On | Adds the link to the logged-in member's own profile |
| **Community header menu** | Off | Adds the link to the Fluent Community top header |

Use a distinct label so members do not confuse signing documents with Fluent Community's file library.

## Member experience

1. A logged-in member opens the link from their profile or the community header.
2. WPsigner sends them to:

   ```text
   https://your-site.com/wps-fc-documents/
   ```

3. The page lists pending and completed documents associated with the user's WordPress email.
4. The member can open a pending signing link or download completed documents.

The endpoint uses the site's WordPress theme rather than rendering inside Fluent Community's application shell.

> **note**
The **Enable Client Portal** setting is not required for this integration. Fluent Community uses the same signer interface through an integration-specific endpoint.

## How document matching works

WPsigner compares the logged-in user's WordPress email with the signer email saved on each document. A member sees no documents when those addresses differ.

The profile action appears only on the member's **own** Fluent Community profile. It is intentionally omitted when they view another member's profile.

## Permissions and security

- Members must be logged in; logged-out visitors are redirected to WordPress login and returned afterward.
- Any WordPress role can use the member endpoint.
- The profile link is restricted to the profile owner.
- WPsigner nonces protect portal actions.
- Settings and connection tests require a WordPress Administrator.
- The documents page sends `noindex, nofollow`, `X-Frame-Options: SAMEORIGIN`, and `X-Content-Type-Options: nosniff` headers.

## Customize the documents URL

Developers can replace the default endpoint URL:

```php
add_filter( 'wps_fluent_community_documents_url', function ( $url ) {
	return home_url( '/my-signing-documents/' );
} );
```

The replacement page must render a compatible signer experience; changing the URL alone does not add the portal shortcode.

## Troubleshooting

### Fluent Community Plugin Not Found

Activate Fluent Community on the same WordPress installation, then reload the settings page.

### The integration shows Setup required

Confirm all three conditions:

1. The Fluent Community module is enabled.
2. Fluent Community is active.
3. **Enable Fluent Community Integration** is on and saved.

### The Documents URL returns 404

1. Save the Fluent Community integration settings again.
2. Go to **Settings → Permalinks** and click **Save Changes** once.
3. Confirm the permalink structure is not **Plain**.
4. Clear page, server, and CDN caches.

### The link does not appear

- Confirm **Profile actions** or **Community header menu** is enabled.
- Profile actions only appear on the current member's own profile.
- Check that the module and integration are both enabled.

### The member sees no documents

Compare the member's WordPress account email with the signer email on the document. They must match exactly.

### Test Connection is rate-limited

Wait one minute and try again. WPsigner limits repeated test and save requests to reduce abuse.

## Related guides

- [Client Portal](/core-features/client-portal/)
- [Team Roles & Permissions](/core-features/team-roles/)
- [Signer Workflows](/core-features/signer-workflows/)

---

# Fluent Forms Integration

> Connect Fluent Forms with WPsigner for content signing, PDF template feeds, or Document Builder feeds — with field prefill and open-for-signing or email delivery.

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/fluent-forms/
Markdown: https://docs.wpsigner.com/md/integrations/fluent-forms.md

Automate document signing from **Fluent Forms** submissions with **WPsigner**. You can generate a PDF from the form answers (content signing), fill a fixed PDF template, or use a **Document Builder** HTML template — then open the signing page immediately or email the signing request.

> **note**
Works with **Fluent Forms Free** (v5.x+) and **Fluent Forms Pro**. No Fluent Forms add-on is required.

---

## Choose your workflow

| Workflow | Best for | Template type | Where you configure it | Feed required? |
|----------|----------|---------------|------------------------|----------------|
| **[Content signing](#content-signing)** | Applications, registrations, agreements built from form answers | None (PDF built from the submission) | Form editor (+ optional feed for CC / title) | No (optional) |
| **[PDF template + feed](#pdf-template--feed)** | Fixed PDF contracts (NDA, lease, offer letter) | WPsigner PDF template | **WPsigner → Integrations → Fluent Forms** | Yes |
| **[Document Builder + feed](#document-builder--fluent-forms)** | Contracts written in Document Builder | Document Builder HTML template | **WPsigner → Fluent · Doc Builder** (addon) | Yes |

> **One feed type per form**
Do **not** enable a **PDF template** feed and a **Document Builder** feed on the **same** Fluent Form. WPsigner blocks saving/enabling the second feed. Using both would create two documents and can break the post-submit redirect.

> **tip**
- Need a signed PDF of the **form answers** with an inline signature? Use **content signing**.
- Need a **pre-designed PDF** with mapped fields? Use **PDF template + feed**.
- Need a document written in **Document Builder**? Use the Document Builder Fluent screen (addon).

---

## How template feeds work

Both **PDF template** and **Document Builder** feeds follow the same delivery idea:

```
Form submitted
  → Feed runs
  → Document created from template
  → Mapped fields prefilled (signature / initials stay empty)
  → After submit:
       • Open signing page immediately  → redirect to /insigner/{token} (no signing email)
       • Send signing request by email → email with signing link (no browser redirect)
```

| Piece | Description |
|-------|-------------|
| **Feed** | Rule that links one Fluent Form to one template |
| **Primary signer** | Name + email fields from the form (required) |
| **Field mapping** | Dropdown → dropdown: template field ↔ Fluent Forms field |
| **After submit** | Open for signing **or** email the signing request |
| **Prefill** | Text/date/name/company/etc. filled from the submission; **signature** and **initials** are never prefilled |

---

## Content signing

Generate a signed PDF from the **form submission content** plus an inline signature. No PDF template upload is required.

### How it works

```
Form submitted → PDF built from answers → Signature applied → Audit trail + Certificate of Completion
```

### Setup

1. Install **Fluent Forms** and **WPsigner** (**3.1.1+** recommended for large forms + OTP).
2. Edit your Fluent Form and add at least:
   - A **Name** field (or Fluent Forms **Names** = first + last)
   - An **Email** field
   - The **WPsigner Signing** field (Draw / Type / Upload)
3. **Publish** the form.

On submit, WPsigner captures the signature(s) and form data, generates the signed PDF, and runs the normal completion flow.

> **Multiple signature fields**
You can add more than one **WPsigner Signing** field (for example Client, Witness, Guarantor). Every field that contains a signature is drawn on the PDF with its own label. Leave a signing field empty only if it is optional in Fluent Forms validation.

> **Name vs Username**
Use a real **person name** field (Name / Names). Do **not** rely on a field labeled only “Username” for the signer name — WPsigner treats that as a login-style field, not the signer’s legal name.

### Optional feed (CC emails / title)

A feed is **optional** for content signing. Create one only if you need:

- **CC emails** on the completion / signed document
- A **custom document title**

Path: **WPsigner → Integrations → Fluent Forms → New Feed**

1. Select a form that includes the **WPsigner Signing** field.
2. Set **Feed Name**, the **Fluent Form**, and optional **CC emails** / document title.
3. Save the feed — you do **not** need to choose a PDF template for content signing.

> **note**
You do **not** pick a “Signing field” in the feed UI. If the form contains **WPsigner Signing**, WPsigner auto-detects it and uses content-signing mode (template options are skipped).

If the feed is misconfigured but the form still has a Signing field, WPsigner falls back to automatic content signing.

### Submission validation

If PDF generation fails (missing signature data, server error, etc.), **the form submission is blocked**. Fluent Forms will not store an orphaned entry without a completed signed document.

---

## WPsigner Signing field + OTP

The **WPsigner Signing** field can require a one-time password before submit.

### Enable OTP

1. Edit the Fluent Form.
2. Select the **WPsigner Signing** field.
3. Set **OTP verification** to **Enabled**.
4. Choose **OTP delivery method**:

| Method | Requirements |
|--------|--------------|
| Email | Email field on the form |
| SMS | Phone field + [Twilio](/integrations/twilio/) configured |
| Email + SMS | Both email and phone fields |
| WhatsApp | Phone field + [WhatsApp](/integrations/whatsapp/) configured |

5. Save and publish.

### Signer experience

1. User fills the form and provides a signature.
2. On submit, an OTP modal appears.
3. User enters the 6-digit code.
4. After **Identity verified**, the form submits and the signed PDF is created.

Submit stays blocked until OTP succeeds. Use E.164 phone format for SMS/WhatsApp.

> **Large forms (many fields)**
Content signing with OTP works on short and long Fluent Forms. After a successful OTP, WPsigner keeps the verification token through Fluent Forms’ AJAX submit (with a browser-session fallback). If you still see “Please verify your identity…” after a green verified state, update to **WPsigner 3.1.1+** and hard-refresh the form page.

---

## Merge tags in Fluent Forms emails

Use these tags in Fluent Forms **email notifications** after content signing:

| Tag | Output |
|-----|--------|
| `{inSigner:signed_pdf_url}` | Direct URL to download the signed PDF |
| `{inSigner:download_button}` | HTML button linking to the signed PDF |

Example body:

```html
<p>Your signed document is ready:</p>
{inSigner:download_button}
```

Tags resolve when the signed PDF URL is available (after successful signing).

---

## PDF template + feed

Use this when you have a **fixed PDF** (WPsigner template) and want form data mapped into fields, then either open the signing UI or email the request.

Admin page: **WPsigner → Integrations → Fluent Forms** (labeled **Fluent Forms · PDF templates** when Document Builder is also installed).

### Prerequisites

1. **WPsigner** **3.1.1+** (dropdown field mapping, delivery modes, content-signing feeds, OTP on large forms)
2. **Fluent Forms** Free v5.x+ or Pro
3. A published Fluent Form with name + email fields
4. A **WPsigner Template** with fields placed on the PDF (signature, name, date, text, etc.)

### Step 1 — Create a WPsigner PDF template

1. Go to **WPsigner → New Document**.
2. Upload your PDF.
3. Add a signer (placeholder name/email is fine; the form will replace them).
4. Place fields (**Signature**, **Name**, **Email**, **Date**, **Text**, etc.).
5. On Review, **Save as Template**.

> **tip**
**Double-click** each PDF field and set a clear **Field name**. Those labels appear in the feed’s **WPsigner template** dropdown. Optionally set a **Mapping name** for Zapier/API — see [Form fields](/core-features/form-fields/#field-name-mapping-name-and-placeholder).

### Step 2 — Create the Fluent Form

Minimum fields:

| Field | Required | Purpose |
|-------|----------|---------|
| Name | Yes | Primary signer name |
| Email | Yes | Primary signer email / signing identity |
| Company, phone, text, etc. | Optional | Available in **Field mapping** |

Do **not** add the **WPsigner Signing** field if you want PDF template mode. If that field is present, WPsigner treats the form as content signing and skips the PDF template feed path.

### Step 3 — Create the feed

1. Go to **WPsigner → Integrations → Fluent Forms**.
2. Click **New Feed** (centered modal).
3. Configure:

#### Feed name

Internal label shown in the feeds list (e.g. `NDA — website form`).

#### Fluent Form

Published form that triggers the feed.

#### CC emails (optional)

Optional copies for completion / signed PDF notices. Leave empty if unused.

#### WPsigner template

PDF template used to create the document.

#### Document title

Supports merge tags:

| Variable | Replaced with |
|----------|----------------|
| `{{signer_name}}` | Signer name from the form |
| `{{signer_email}}` | Signer email from the form |
| `{{date}}` | Current date |
| `{{company_name}}` | Company name when available |

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

#### Primary signer

| Control | Meaning |
|---------|---------|
| **Name field** | Fluent Forms field for signer name |
| **Email field** | Fluent Forms field for signer email |

Both are required. If either is empty or the email is invalid on submit, that feed run is skipped.

#### Field mapping

Map **template fields → form fields** with **two dropdowns** (no typing `custom.*`):

| Side | Source | What you see |
|------|--------|--------------|
| Left | **WPsigner template** | Human-readable field label (user label, or default type name such as Name / Date / Email). Duplicates become `Name (2)`. |
| Right | **Fluent Forms** | Form field labels |

Rules:

- Click **Add** for each row.
- **Signature** and **initials** template fields are **not** listed and are **never** prefilled — the signer completes them on the signing page.
- Values from the submission are written into the created document before delivery.

#### After submit

| Option | What happens |
|--------|----------------|
| **Open signing page immediately** | Document is set to **sent**, fields are prefilled, **no signing email** is sent. Fluent Forms confirmation redirects the browser to `/insigner/{token}` so the submitter can sign right away. |
| **Send signing request by email** | Document is set to **sent** and each signer (including additional signers, if configured) receives the signing-request email. **No** browser redirect to the signing page. |

> **Legacy feeds**
Older feeds that only had “Auto-send” are interpreted as: Auto-send on → **email**; Auto-send off → **open signing page immediately**.

#### Feed enabled

When disabled, the feed does not run on submissions.

4. Click **Save Feed**.

### Step 4 — Test

**Open for signing**

1. Submit the form on the front end.
2. You should be redirected to the WPsigner signing page.
3. Prefillable fields (name, date, text, etc.) should already be filled.
4. Complete **Signature** (and initials if present).

**Email**

1. Submit the form.
2. Check the signer inbox for the signing request.
3. Open the link and confirm prefilled fields + empty signature.

Also verify under **WPsigner → Documents** that the new document exists with status **sent**.

---

## Document Builder + Fluent Forms

If the **WPsigner Document Builder** addon is installed (**1.5.2+** recommended), you can map a Fluent Form to a **Document Builder** template instead of a PDF template.

Admin page: **WPsigner → Fluent · Doc Builder** (also linked from the PDF Fluent Forms page under “Which documents do you create from Fluent Forms?”).

### What it does

1. On submit, clones the Document Builder template into a new document.
2. Prefills mapped fields from the Fluent Forms submission.
3. Sets status to **sent** without requiring the admin wizard.
4. Delivers via the same two options:
   - **Open signing page immediately** (redirect, no email)
   - **Send signing request by email**

### Feed settings (Document Builder)

| Setting | Description |
|---------|-------------|
| Fluent Form | Published form |
| Document Builder template | Saved IDB template |
| Document title | Optional; entry id may be appended for uniqueness |
| Signer name / email fields | Dropdowns of form fields |
| Field mapping | Form field label → document field label (both dropdowns) |
| After submit | Open for signing **or** email |
| Enabled | On/off |

> **caution**
An enabled Document Builder feed on a form blocks enabling a PDF-template feed on that same form (and the reverse). Disable one before enabling the other.

---

## Integration settings page

**WPsigner → Integrations → Fluent Forms**

- Connection status and counts
- Feed list (with delivery badges such as **Open for signing** / **Email signing request**)
- When Document Builder is active: chooser cards linking **PDF templates** vs **Document Builder**, plus a warning listing forms already used by the other feed type

---

## Managing feeds

### Edit

**Edit** on the feed card → change settings → **Save Feed**.

### Enable / disable

Toggle on the card. Enabling is blocked if the same form already has an enabled feed of the other type (PDF vs Document Builder).

### Delete

Trash icon → confirm. Existing documents are not deleted.

---

## Multiple feeds

- **Multiple PDF feeds on different forms** — supported.
- **Multiple PDF feeds on the same form** — all enabled PDF feeds for that form can run (template mode). Prefer one clear feed per form unless you intentionally want multiple documents.
- **PDF feed + Document Builder feed on the same form** — **not allowed** while both are enabled.

---

## Confirmation behavior

### Open signing page immediately

WPsigner hooks Fluent Forms confirmation (`fluentform/submission_confirmation`) and sets `redirectUrl` to the primary signer’s signing URL. The submitter lands on the signing UI after AJAX submit.

You do not need a custom Fluent Forms redirect URL for this mode.

### Send by email

Fluent Forms shows its normal confirmation message. Point users to check email if you customize that message.

### Content signing

Use Fluent Forms confirmations / notifications as usual. Prefer merge tags `{inSigner:signed_pdf_url}` / `{inSigner:download_button}` in notifications after the PDF exists.

---

## Use cases

| Scenario | Recommended setup |
|----------|-------------------|
| Kiosk / same-device signing after a form | PDF or Document Builder feed → **Open signing page immediately** |
| Remote signer after website form | PDF or Document Builder feed → **Send signing request by email** |
| Application form that *is* the contract | **Content signing** (WPsigner Signing field on the form) |
| Fixed NDA PDF with company/phone prefilled | PDF template feed + field mapping dropdowns |
| HTML contract from Document Builder | Document Builder Fluent feed |

---

## Troubleshooting

| Issue | Cause | Solution |
|-------|-------|----------|
| No document created | Feed disabled or incomplete | Enable feed; set form, template, signer name/email |
| Save blocked: “Please fill in Feed Name and Form” | Content-signing feed treated as if a template were required | Update to **3.1.1+**. For content signing you only need Feed Name + Form (no template) |
| OTP shows verified, then “Please verify your identity…” | Older core lost the OTP token on large AJAX submits | Update to **3.1.1+**, hard-refresh the form, complete OTP again |
| Wrong signer name on the document | Mapped a Username / login field instead of Name | Map the **Name** or **Names** field; avoid “Username” |
| Save/enable blocked mentioning Document Builder / PDF feed | Same form already has the other feed type enabled | Disable the other feed or use a different form |
| Not redirected after submit | Delivery set to email, or feed failed | Use **Open signing page immediately**; confirm name/email valid |
| No signing email | Delivery set to open-for-signing, or SMTP issue | Use **Send signing request by email**; check WordPress mail |
| Prefill empty | Field mapping missing or wrong fields | Re-map with dropdowns; double-click template fields and set clear **Field names** |
| Dropdown shows only type names | Template field labels empty | Edit the template → double-click each field → set **Field name** |
| Signature already filled | — | Signature/initials are never prefilled by design |
| “Fluent Forms is not installed” | Plugin inactive | Install/activate Fluent Forms |
| Form submission blocked | Content signing PDF/OTP failure | Check Signing field, OTP channels, `debug.log` |
| Merge tags empty | Used too early | Use in post-submission notifications |
| Rapid tests stop creating docs | Rate limit | Max **10 documents per minute per form** |

### Debug log

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

Check `wp-content/debug.log` for `WPS`, `FluentForms`, or `IDB FluentForms` entries.

---

## Compatibility

| Component | Supported |
|-----------|-----------|
| Fluent Forms Free | v5.0+ |
| Fluent Forms Pro | All versions |
| WPsigner (PDF feeds, content signing, OTP on large forms, dropdown mapping) | **3.1.1+** recommended |
| WPsigner Document Builder (Fluent Doc Builder feeds) | **1.5.2+** recommended |
| WordPress | 5.8+ |
| PHP | 7.4+ |

> **note**
Template feeds use `fluentform/submission_inserted`. Open-for-signing uses `fluentform/submission_confirmation` (and the legacy `fluentform_submission_confirmation` alias) to set the redirect URL. These hooks are available in Free and Pro.

---

## Next steps

- [Smart Signing Forms](/addons/smart-signing-forms/) — Standalone signing forms without Fluent Forms
- [Creating documents](/core-features/creating-documents/) — Document statuses and actions
- [Form fields](/core-features/form-fields/) — Field types on templates
- [WhatsApp](/integrations/whatsapp/) — Deliver signing links via WhatsApp
- [REST API](/api/) — Programmatic integrations

---

# FluentCRM

> Sync document signing events with FluentCRM contacts

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/fluentcrm/
Markdown: https://docs.wpsigner.com/md/integrations/fluentcrm.md

Connect WPsigner with **FluentCRM** to automatically sync signing events with your CRM contacts — apply tags, add to lists, and track activity.

> **tip**
FluentCRM is a self-hosted WordPress CRM plugin. This integration uses **direct PHP hooks** — no external API keys or HTTP requests required.

---

## How It Works

```
WPsigner Event → FluentCRM Contact Action
├── Signer Added    → Create/update contact, add to list
├── Document Signed → Apply "signed" tag, log activity
└── All Complete    → Apply "completed" tag to all signers
```

---

## Requirements

- WPsigner **2.1.0+**
- [FluentCRM](https://fluentcrm.com/) plugin (Free or Pro)
- Both plugins active on the same WordPress installation

---

## Setup

### Step 1: Install FluentCRM

1. Install and activate the FluentCRM plugin
2. Complete the FluentCRM setup wizard if prompted

### Step 2: Create Tags (Recommended)

1. Go to **FluentCRM → Tags**
2. Create tags for signing events:
   - `Document Signed` — applied when a signer signs
   - `All Signatures Complete` — applied when all signers finish

### Step 3: Configure WPsigner

1. Go to **WPsigner → Integrations → FluentCRM**
2. Click **Test Connection** to verify FluentCRM is detected
3. Configure:

| Setting | Description |
|---|---|
| **Enable** | Turn on the integration |
| **Tag on Signature** | Tag to apply when a signer signs a document |
| **Tag on Complete** | Tag to apply when all signatures are complete |
| **Add to List** | List to add signers to when added to a document |
| **Track Activity** | Log signing events in the contact timeline |

4. Click **Save Settings**

---

## Events & Actions

| WPsigner Event | FluentCRM Action |
|---|---|
| **Signer Added** | Create or update contact; add to selected list |
| **Document Signed** | Apply "signed" tag; log note in contact activity |
| **All Signatures Complete** | Apply "completed" tag to all signers; log note |

---

## Contact Management

### Automatic Contact Creation

When a signer is added to a document, WPsigner will:
1. Search FluentCRM for an existing contact by email
2. If found, update the existing contact
3. If not found, create a new contact with status **Subscribed**

### Activity Notes

With **Track Activity** enabled, WPsigner logs notes in the contact timeline:
- `"Added as signer to document: Service Agreement"`
- `"Signed document: Service Agreement"`
- `"All signatures complete on: Service Agreement"`

---

## Security

| Feature | Details |
|---|---|
| **No API Keys** | Uses FluentCRM's PHP API directly |
| **Nonce Verification** | All AJAX requests verified with nonces |
| **Rate Limiting** | Test: 5/min, Save: 10/min per user |
| **Capability Check** | Requires `manage_options` for settings |

---

## Troubleshooting

| Issue | Solution |
|---|---|
| "FluentCRM Plugin Not Found" | Install and activate FluentCRM |
| Tags not appearing | Create tags in FluentCRM → Tags first |
| Contact not created | Verify signer has a valid email address |
| Activity not logged | Enable **Track Activity** in settings |

---

## Next Steps

- [LearnDash Integration](/integrations/learndash/) — Gate course access with signatures
- [Telegram Integration](/integrations/telegram/) — Get signing notifications
- [Slack Integration](/integrations/slack/) — Team notifications

---

# Google Drive Integration

> Automatically backup signed PDFs to Google Drive with WPsigner's one-click integration. No configuration or API keys needed.

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/google-drive/
Markdown: https://docs.wpsigner.com/md/integrations/google-drive.md

Automatically save a backup copy of every signed PDF to your Google Drive. WPsigner's Google Drive integration uses a **one-click OAuth connection** — no API keys, no manual configuration, and no developer setup required.

---

## How It Works

```
Document Signed → PDF Finalized → Backup Uploaded to Google Drive → Audit Trail Logged
```

When a document is fully signed and completed, WPsigner automatically:

1. Generates the final signed PDF
2. Creates a folder called **"WPsigner Signed Backups"** in your Google Drive (first time only)
3. Uploads the signed PDF to that folder
4. Logs the backup to the document's audit trail

| Feature | Details |
|---------|---------|
| **Connection** | One-click OAuth — no API keys needed |
| **Trigger** | Automatic backup when a document is completed |
| **Folder** | `WPsigner Signed Backups` (created automatically) |
| **File naming** | `{Document Title}_{Date}.pdf` |
| **Privacy** | Files are private — only your Google account can access them |
| **Encryption** | OAuth tokens are encrypted using WordPress salts |
| **Token refresh** | Access tokens auto-refresh — no re-authentication needed |

> **note**
The original signed PDF always remains stored on your WordPress server. Google Drive serves as an **additional backup** — it does not replace local storage.

---

## Prerequisites

- **WPsigner** v2.0.0 or later
- A **Google account** (personal or Google Workspace)
- **WordPress admin** access (the `manage_options` capability is required)

---

## Step 1: Connect Google Drive

### Navigate to the Integration

You can access the Google Drive settings in two ways:

**Option A — From the Integrations page:**
1. Go to **WPsigner → Integrations** in your WordPress admin
2. Find the **Google Drive** card under Cloud Storage
3. Click **Configure**

**Option B — From Settings:**
1. Go to **WPsigner → Settings**
2. Click the **Google Drive** tab

### Authorize WPsigner

1. On the Google Drive settings page, you'll see the **"Connect with Google Drive"** button
2. Click the button — you will be redirected to Google's authorization page
3. **Sign in** to your Google account (or select one if you're logged into multiple accounts)
4. **Review the permissions** — WPsigner requests access to:
   - Create and manage files in Google Drive that WPsigner creates
   - View your email address (to display which account is connected)
5. Click **Allow** to grant access
6. You'll be redirected back to your WordPress admin with a **"Successfully connected!"** confirmation message

> **tip**
WPsigner uses a secure OAuth proxy via Cloudflare Workers to handle the authorization flow. Your Google credentials are never stored on your WordPress server — only encrypted OAuth tokens are saved.

---

## Step 2: Verify the Connection

After connecting, the settings page shows:

### Connection Status
- **Green indicator**: Connected to Google Drive
- **Email address**: The Google account connected (e.g., `you@gmail.com`)

### Connection Details
| Item | Value |
|------|-------|
| **Backup Folder** | `WPsigner Signed Backups` |
| **Privacy** | Files are private (only you can access) |
| **Auto-backup** | PDFs are saved automatically after signing |

### Test the Connection

Click the **"Test Connection"** button to verify everything works:

1. WPsigner uploads a small test file to your Google Drive
2. If successful, you'll see a confirmation message: **"✓ Test file uploaded successfully"**
3. You can check your Google Drive → **"WPsigner Signed Backups"** folder to see the test file

> **caution**
If the test upload fails, check your internet connection and ensure Google Drive is accessible from your server. Some hosting providers block outgoing HTTP requests — contact your host if the test consistently fails.

---

## Step 3: Use It

Once connected, **there's nothing else to do**. The integration works automatically:

1. **Create and send documents** for signing as you normally would
2. **Signers complete** the signing process
3. When all signers have signed, the document status changes to **Completed**
4. WPsigner **automatically uploads** the signed PDF to your Google Drive
5. The upload is **logged in the audit trail** for compliance tracking

### Where to Find Your Backups

1. Open [Google Drive](https://drive.google.com)
2. Navigate to **"WPsigner Signed Backups"** folder
3. You'll see all your signed PDFs organized by filename

### File Naming Convention

Backup files follow this naming pattern:

```
{Document_Title}_{Date}.pdf
```

**Examples:**
- `Service_Agreement_2026-02-07.pdf`
- `NDA_John_Smith_2026-02-07.pdf`
- `Employment_Contract_2026-02-07.pdf`

> **note**
You can customize the filename format using the `wps_google_drive_filename` WordPress filter. See the [Developer Hooks](#developer-hooks) section below for details.

---

## Managing the Integration

### Disconnect Google Drive

If you need to disconnect:

1. Go to **WPsigner → Integrations → Google Drive** (or **Settings → Google Drive** tab)
2. Click the **"Disconnect"** button
3. Confirm the disconnection

After disconnecting:
- No more backups will be uploaded to Google Drive
- Existing backups in Google Drive **are not deleted** — they remain in your Drive
- You can reconnect at any time by clicking "Connect with Google Drive" again

### Reconnect with a Different Account

To switch Google accounts:

1. **Disconnect** the current account
2. **Connect** again — you'll be prompted to choose a Google account
3. Select the new account and authorize

---

## Security & Privacy

### Token Storage

| Security Layer | Description |
|----------------|-------------|
| **AES-256-CBC encryption** | OAuth tokens are encrypted before storage |
| **WordPress salts** | Encryption keys derive from your `wp-config.php` salts |
| **Random IV** | Each encryption uses a unique initialization vector |
| **No credentials stored** | Your Google password is never stored — only OAuth tokens |

### Permissions Scope

WPsigner only requests the minimum necessary Google API scopes:

| Scope | Purpose |
|-------|---------|
| `drive.file` | Create and manage files that WPsigner uploads (cannot access your other Drive files) |
| `userinfo.email` | Display which Google account is connected |

> **important**
WPsigner **cannot** read, modify, or delete any other files in your Google Drive. The `drive.file` scope only grants access to files that WPsigner itself has created.

### Token Refresh

- Access tokens expire after **1 hour**
- WPsigner automatically refreshes tokens using the stored refresh token
- This means you **never need to re-authorize** — the connection stays active
- If a refresh fails, a transient error is logged but no data is lost

---

## Developer Hooks

WPsigner provides WordPress hooks for developers who want to customize the Google Drive integration:

### Filter: Customize Backup Filename

```php
// Change the filename format for Google Drive backups
add_filter('wps_google_drive_filename', function($file_name, $document_id, $document) {
    // Example: Include document ID in filename
    return "WPS-{$document_id}_{$document->title}_" . wp_date('Y-m-d') . '.pdf';
}, 10, 3);
```

### Action: After Successful Upload

```php
// Perform actions after a PDF is backed up to Google Drive
add_action('wps_google_drive_uploaded', function($document_id, $file_id, $file_name) {
    // Example: Send a notification
    error_log("Document #{$document_id} backed up to Google Drive: {$file_name}");
    
    // Example: Store the Google Drive file ID in document meta
    update_post_meta($document_id, '_gdrive_file_id', $file_id);
}, 10, 3);
```

---

## Troubleshooting

### Common Issues

| Issue | Cause | Solution |
|-------|-------|----------|
| "Connect with Google Drive" button doesn't work | Authorization URL not generated | Refresh the page and try again |
| Redirect after authorization fails | Callback URL blocked | Ensure your site is accessible publicly (Google needs to redirect back) |
| Test upload fails | Outgoing HTTP requests blocked | Contact your hosting provider to allow requests to `googleapis.com` |
| PDFs not appearing in Google Drive | Integration disconnected or tokens expired | Check the connection in Settings → Google Drive |
| "Failed to decode OAuth tokens" error | Corrupted callback data | Try disconnecting and reconnecting |
| Backup folder not found in Drive | Folder was manually deleted | WPsigner will recreate it on the next upload |

### Checking the Audit Trail

Every Google Drive backup is logged in the document's audit trail:

1. Go to **WPsigner → Documents**
2. Open the completed document
3. Check the **Audit Trail** section
4. Look for the entry: **"PDF backed up to Google Drive: {filename}"**

If this entry is missing, the upload may have failed silently. Check your WordPress debug log for more details.

### Debug Logging

Enable WordPress debug logging to troubleshoot connection issues:

```php
// In wp-config.php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
```

Then check `wp-content/debug.log` for entries related to Google Drive.

---

## Frequently Asked Questions

### Does it work with Google Workspace (G Suite)?
**Yes.** The integration works with personal Google accounts and Google Workspace (formerly G Suite) accounts.

### Does it count against my Google Drive storage?
**Yes.** Uploaded PDFs count against your Google Drive storage quota. Signed PDFs are typically small (100 KB–2 MB each), so this is unlikely to be an issue.

### What happens if my Google Drive is full?
The upload will fail, and an error will be logged. The signed PDF remains safely stored on your WordPress server. You can free up space in Google Drive and future uploads will resume automatically.

### Can I backup to a specific folder?
The folder is automatically set to **"WPsigner Signed Backups"**. Currently, the folder name cannot be changed through the UI, but developers can modify the behavior using WordPress filters.

### Do older documents get backed up retroactively?
**No.** Only documents completed after the integration is connected are backed up. If you need to backup older documents, you can download the signed PDFs manually and upload them to Google Drive.

### Is the connection shared between WordPress users?
**Yes.** The Google Drive connection is site-wide. All WordPress administrators share the same connection. Documents completed by any user will be backed up to the same Google Drive account.

---

## Next Steps

- [Creating Documents](/core-features/creating-documents/) — Learn how to create and send documents
- [Audit Trails](/digital-identity/audit-trails/) — Track document activity
- [Fluent Forms Integration](/integrations/fluent-forms/) — Auto-create documents from form submissions
- [WhatsApp Integration](/integrations/whatsapp/) — Send signing links via WhatsApp

---

# Gravity Forms

> Connect Gravity Forms with WPsigner to automatically create signing documents from form submissions.

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/gravity-forms/
Markdown: https://docs.wpsigner.com/md/integrations/gravity-forms.md

> **note**
This integration requires **Gravity Forms** (any license tier) installed and active on your WordPress site.

## Overview

The Gravity Forms integration for WPsigner allows you to **automatically generate signing documents** when a Gravity Forms form is submitted. Using a **feed-based system**, you connect a Gravity Forms form to a WPsigner template, map form fields to signer information and template variables, and optionally auto-send the document for signing.

### Key Features

- 📝 **Feed-based Configuration** — Create multiple feeds per form for different templates
- ✍️ **Signer Mapping** — Map Name and Email fields to document signers
- 🔗 **Variable Mapping** — Pass custom form data (company, phone, etc.) to templates
- 📧 **Auto-send** — Optionally send signing emails immediately after submission
- 🔒 **Security** — Nonce verification, capability checks, rate limiting, input sanitization

---

## Setup

1. **Install Gravity Forms**

   Ensure Gravity Forms is installed and activated. Visit [gravityforms.com](https://www.gravityforms.com/) to get a license.

2. **Navigate to Integration Settings**

   Go to **WPsigner → Integrations** and click **Configure** on the Gravity Forms card.

3. **Create a Feed**

   Click **New Feed** and configure:
   - **Feed Name** — A descriptive name (e.g., "Service Contract")
   - **Gravity Forms Form** — Select the form to trigger document creation
   - **WPsigner Template** — Select the template to use
   - **Primary Signer** — Map the Name and Email fields from the form
   - **Variable Mapping** — Map form fields to template variables (optional)
   - **Auto-send** — Enable to send immediately (optional)

4. **Test the Integration**

   Submit the form and verify a document is created in **WPsigner → Documents**.

---

## Feed Configuration

### Primary Signer

Map form fields to extract the signer's name and email:

| Setting | Description |
|---------|-------------|
| **Name Field** | The form field containing the signer's full name |
| **Email Field** | The form field containing the signer's email address |

> **tip**
Gravity Forms splits Name fields into sub-inputs (First, Last). WPsigner detects these automatically — you'll see options like "Name (First)" and "Name (Last)" in the field selector.

### Variable Mapping

Map form fields to template variables using the `custom.*` prefix:

| Template Variable | Form Field Example |
|---|---|
| `custom.company` | Company Name field |
| `custom.phone` | Phone Number field |
| `custom.address` | Address field |
| `custom.amount` | Total field |

Variables appear in your template as `{{custom.company}}`, `{{custom.phone}}`, etc.

### Document Title

Use dynamic placeholders in the document title:

```
Contract - {{signer_name}} - {{date}}
```

Available placeholders: `{{signer_name}}`, `{{signer_email}}`, `{{date}}`, `{{company_name}}`

---

## Security

The integration implements comprehensive security measures:

| Layer | Implementation |
|-------|---------------|
| **Authentication** | Nonce verification on all AJAX handlers |
| **Authorization** | `manage_options` capability required for feed management |
| **Rate Limiting** | Maximum 10 documents per minute per form |
| **Input Sanitization** | All fields sanitized with appropriate WordPress functions |
| **Email Validation** | Double validation with `sanitize_email()` + `is_email()` |
| **XSS Prevention** | Output escaping with `esc_html()`, `esc_attr()`, `esc_url()` |

---

## Developer Hooks

### `wps_gravityforms_document_created`

Fires after a document is created from a form submission.

```php
add_action('wps_gravityforms_document_created', function($document_id, $entry_id, $entry, $form, $feed) {
    // Custom logic after document creation
    // e.g., update CRM, send notification, log event
}, 10, 5);
```

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `$document_id` | `int` | The created WPsigner document ID |
| `$entry_id` | `int` | The Gravity Forms entry ID |
| `$entry` | `array` | The Entry Object with submitted values |
| `$form` | `array` | The Form Object with form configuration |
| `$feed` | `array` | The feed configuration that triggered creation |

---

## Use Cases

| Scenario | Setup |
|----------|-------|
| **Client onboarding** | Contact form → NDA template → Auto-send |
| **Quote requests** | Quote form → Service agreement template |
| **Job applications** | Application form → Offer letter template |
| **Event registration** | Registration form → Waiver template → Auto-send |
| **Rental agreements** | Booking form → Rental agreement template → Auto-send |
| **Service contracts** | Order form → Service contract template |
| **Vendor agreements** | Vendor intake form → Vendor contract template → Auto-send |
| **Consent forms** | Patient/client form → Consent document template |

> **tip**
You can create multiple feeds for the same form to generate different documents simultaneously. For example, a vendor intake form could produce both an NDA and a service agreement in a single submission.

---

## Multiple Feeds

You can create **multiple feeds** for the same form or template:

- **One form, multiple templates**: A single form submission generates different documents based on different templates
- **Multiple forms, one template**: Different forms can all use the same document template with different signer data
- **One-to-one**: Each form has its own dedicated template

Each feed operates independently. When a form is submitted, all active feeds linked to that form will trigger.

---

## Compatibility

| Component | Supported Versions |
|-----------|-------------------|
| **Gravity Forms** | 2.5+ (any license tier) |
| **WPsigner** | 1.5.0+ |
| **WordPress** | 5.8+ |
| **PHP** | 7.4+ |

> **note**
Gravity Forms requires a paid license. All tiers (Basic, Pro, Elite) are supported. The integration uses the `gform_after_submission` hook, which is available in all versions.

---

## Troubleshooting

### Common Issues

| Issue | Cause | Solution |
|-------|-------|----------|
| No document created after submission | Feed not active | Verify the feed is **enabled** (green "Active" badge) |
| No document created | Form/template mismatch | Check that the form ID matches the feed configuration |
| Missing signer data | Fields not mapped | Ensure the signer Name and Email fields are mapped |
| Signer not receiving email | Auto-send disabled | Enable "Auto-send" in the feed settings |
| Signer not receiving email | SMTP issue | Check your WordPress email configuration (use an SMTP plugin) |
| Sub-fields not appearing | Complex field format | Look for labels like "Name (First)" in the field selector |
| "Gravity Forms is not installed" | Plugin not active | Install and activate Gravity Forms with a valid license from [gravityforms.com](https://www.gravityforms.com/) |
| Rate limit exceeded | Too many rapid submissions | Wait 60 seconds — limit is 10 documents/min per form |

### Checking Error Logs

If documents are not being created, check the WordPress debug log:

1. Enable debug logging in `wp-config.php`:

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

2. Check the log file at `wp-content/debug.log`
3. Look for entries starting with `[WPsigner GravityForms]`

---

## Next Steps

- [Templates](/core-features/form-fields/) — Learn more about creating and managing templates
- [Document Workflow](/core-features/creating-documents/) — Understanding document statuses and actions
- [REST API](/api/) — For advanced programmatic integrations
- [WhatsApp Integration](/integrations/whatsapp/) — Send signing links via WhatsApp
- [Fluent Forms](/integrations/fluent-forms/) — Alternative form plugin integration
- [Elementor Forms](/integrations/elementor-forms/) — Build forms with Elementor Pro
- [Automation](/integrations/automation/) — Connect with workflow automation platforms

---

# HubSpot

> Sync document signing events with HubSpot CRM

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/hubspot/
Markdown: https://docs.wpsigner.com/md/integrations/hubspot.md

Automatically sync signing events with **HubSpot CRM** — find or create contacts, log timeline activities, and update custom properties when documents are signed.

> **note**
This integration requires a [HubSpot](https://www.hubspot.com/) account (Free or paid) and a Private App with CRM scopes. No HubSpot paid plan features are required beyond CRM contacts.

---

## Requirements

| Requirement | Details |
|---|---|
| WPsigner | 2.1.0+ |
| HubSpot account | Free or any paid tier |
| HubSpot Private App | With CRM contact scopes |
| WordPress | 5.8+ |
| PHP | 7.4+ |

---

## Setup

### Step 1: Create a Private App in HubSpot

1. Log in to your HubSpot account
2. Go to **Settings → Integrations → Private Apps**
3. Click **Create a private app**
4. Name it (e.g., "WPsigner Integration")
5. Under **Scopes**, enable the following:
   - `crm.objects.contacts.read` — Search and read contact records
   - `crm.objects.contacts.write` — Create contacts and update properties
6. Click **Create app** and copy the generated access token

> **caution**
Store your access token securely. It will only be shown once. If you lose it, you'll need to regenerate a new token from the Private App settings.

### Step 2: Configure WPsigner

1. Go to **WPsigner → Integrations → HubSpot**
2. Paste the **Private App Token** into the token field
3. Toggle the desired sync options:
   - **Create Contact** — Automatically create a new HubSpot contact if the signer's email is not found
   - **Create Note** — Add a timeline note to the contact record when a document is signed
   - **Update Properties** — Update custom HubSpot properties with signing data
4. Click **Save Settings**
5. Click **Test Connection** to verify the token and scopes are correct

> **tip**
Use the **Test Connection** button after saving. It validates that your token is active, the scopes are correct, and WPsigner can reach the HubSpot API.

---

## What Happens When a Document Is Signed

When a signer completes a document, WPsigner triggers the following workflow automatically:

```
Document signed by signer
        ↓
WPsigner searches HubSpot for contact by signer email
        ↓
Contact not found? → Create new contact (if enabled)
        ↓
Create timeline note on contact record (if enabled)
        ↓
Update custom properties (if enabled)
        ↓
Log event to WPsigner audit trail
```

| Step | Action | Details |
|------|--------|---------|
| 1 | **Search contact** | HubSpot is searched by the signer's email address |
| 2 | **Create contact** (if enabled) | If no contact is found, a new one is created with the signer's name and email |
| 3 | **Create timeline note** | A note is added to the contact's activity timeline |
| 4 | **Update properties** | Custom properties are updated with signing metadata |
| 5 | **Audit log** | The sync event is logged to the WPsigner audit trail |

### Timeline Note Format

When a document is signed, WPsigner creates a note on the contact's timeline with the following information:

```
[WPsigner] Document Signed

Document: Service Agreement - John Smith
Signed by: John Smith (john@example.com)
Date: 2026-03-06 14:30:00
Status: Completed
```

This note appears in the contact's **Activity** tab in HubSpot, making it easy for your sales or operations team to see signing history at a glance.

---

## Custom Properties

WPsigner can update custom HubSpot contact properties when documents are signed. This is useful for tracking signing activity, segmenting contacts, and triggering HubSpot workflows.

### Default Properties

When **Update Properties** is enabled, WPsigner updates these properties automatically:

| Property | Type | Value |
|----------|------|-------|
| `wpsigner_last_signed` | Date | Timestamp of the most recent signing |
| `wpsigner_last_document` | String | Title of the most recently signed document |

### Setting Up Custom Properties in HubSpot

Before WPsigner can update these properties, they must exist in your HubSpot account:

1. Go to **HubSpot → Settings → Properties**
2. Click **Create property**
3. Set the **Object type** to **Contact**
4. Set the **Group** to any group (e.g., "Contact information" or create a "WPsigner" group)
5. Create the following properties:

| Property Name | Internal Name | Field Type |
|---------------|---------------|------------|
| WPsigner Last Signed | `wpsigner_last_signed` | Date picker |
| WPsigner Last Document | `wpsigner_last_document` | Single-line text |

6. Click **Create** for each property

> **important**
The **internal name** must match exactly. HubSpot generates the internal name from the property name, but you should verify it matches the values in the table above. Internal names are lowercase with underscores.

### Using Properties in HubSpot Workflows

Once properties are being updated, you can use them in HubSpot automations:

- **Trigger workflows** when `wpsigner_last_signed` is updated (e.g., send a follow-up email)
- **Create smart lists** of contacts who signed documents in the last 30 days
- **Build reports** on signing activity using HubSpot's reporting tools
- **Set up notifications** for your team when a key document is signed

---

## Use Cases

| Scenario | Setup |
|----------|-------|
| **Sales contract tracking** | Sync signed contracts → Track close dates in HubSpot |
| **Client onboarding** | Auto-create contact → Log NDA signing → Trigger onboarding workflow |
| **Lead qualification** | Signed document updates contact properties → Triggers lead score update |
| **Renewal management** | Track last signed date → Trigger renewal reminders via HubSpot workflow |
| **Compliance tracking** | Log all signing events on contact timeline → Audit trail in HubSpot |
| **HR onboarding** | Employee signs offer letter → Contact created → Property updated for HR team |
| **Vendor management** | Vendor signs agreement → Timeline note → Notify procurement team |

> **tip**
Combine HubSpot properties with HubSpot Workflows to create powerful automations. For example, when `wpsigner_last_signed` is updated, automatically send a "Thank you" email or notify the account manager.

---

## Security

| Feature | Details |
|---------|---------|
| **Token encryption** | Private App token is encrypted with AES-256-GCM at rest in the WordPress database |
| **Rate Limiting** | Test connection: 5/min, Save settings: 10/min per user |
| **Capability Check** | `manage_options` required for all configuration changes |
| **Nonce Verification** | All AJAX requests verified with WordPress nonces |
| **API Communication** | All requests to HubSpot API use HTTPS |
| **No PII in Logs** | Error logs never contain contact names or email addresses |

---

## Troubleshooting

### Common Issues

| Issue | Cause | Solution |
|-------|-------|----------|
| "Connection failed" on test | Invalid token | Regenerate the Private App token in HubSpot and re-enter it |
| "Connection failed" on test | Missing scopes | Verify the Private App has both `crm.objects.contacts.read` and `crm.objects.contacts.write` scopes |
| Contact not created | "Create Contact" disabled | Enable the "Create Contact" toggle in WPsigner settings |
| Timeline note not appearing | "Create Note" disabled | Enable the "Create Note" toggle in WPsigner settings |
| Properties not updating | Properties don't exist in HubSpot | Create the custom properties in HubSpot first (see Custom Properties section) |
| Properties not updating | Internal name mismatch | Verify the property internal names match exactly: `wpsigner_last_signed`, `wpsigner_last_document` |
| Duplicate contacts | Multiple emails for same person | HubSpot matches by email — ensure signers use consistent email addresses |
| Rate limit error from HubSpot | Too many API calls | WPsigner respects HubSpot API limits. If you process many documents simultaneously, events are queued |

### Checking Error Logs

If sync events are not working, check the WordPress debug log:

1. Enable debug logging in `wp-config.php`:

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

2. Check the log file at `wp-content/debug.log`
3. Look for entries starting with `[WPsigner HubSpot]`

You can also check the **WPsigner → Audit** page for sync event logs with status indicators.

---

## Compatibility

| Component | Supported Versions |
|-----------|-------------------|
| **HubSpot** | Free CRM, Starter, Professional, Enterprise |
| **HubSpot API** | v3 (Private Apps) |
| **WPsigner** | 2.1.0+ |
| **WordPress** | 5.8+ |
| **PHP** | 7.4+ |

> **note**
The integration uses HubSpot's v3 API via Private Apps. Legacy API keys (deprecated by HubSpot) are not supported. OAuth-based connections are not required — a Private App token is sufficient.

---

## Next Steps

- [Pipedrive](/integrations/pipedrive/) — Pipedrive CRM sync integration
- [Zapier](/integrations/zapier/) — Connect to 5000+ apps via Zapier
- [Make](/integrations/make/) — Visual automation with Make (Integromat)
- [n8n](/integrations/n8n/) — Self-hosted workflow automation
- [Templates](/core-features/form-fields/) — Learn more about creating templates
- [Document Workflow](/core-features/creating-documents/) — Understanding document statuses and actions
- [REST API](/api/) — For advanced programmatic integrations

---

# LearnDash

> Require document signatures before course enrollment or completion

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/learndash/
Markdown: https://docs.wpsigner.com/md/integrations/learndash.md

Integrate WPsigner with **LearnDash LMS** to require signed enrollment agreements, waivers, or compliance documents before students can access course content.

> **note**
LearnDash is a WordPress LMS plugin. This integration uses **direct PHP hooks** — no external API keys required.

---

## How It Works

```
Student Enrolls → Signing Prompt → Signs Document → Course Unlocked
                      ↓
       (Access blocked until signed)
```

WPsigner creates a personalized enrollment agreement from a template and gates course access until the student signs.

---

## Use Cases

| Scenario | Description |
|----------|-------------|
| **Enrollment agreements** | Require students to sign terms and conditions before accessing paid course content |
| **Compliance training** | Gate mandatory training modules behind signed compliance acknowledgments (OSHA, HIPAA, GDPR) |
| **Certification programs** | Collect signed honor-code or exam-integrity agreements before issuing certificates |
| **Corporate onboarding** | Require new employees to sign NDA or policy documents as part of an onboarding course |
| **Waivers and liability** | Collect liability waivers before students access physical-activity or lab-based courses |
| **Continuing education** | Require signed attestation of professional credentials before granting CE credits |

---

## Requirements

- WPsigner **2.1.0+**
- [LearnDash LMS](https://www.learndash.com/) plugin (v3.0+)
- Both plugins active on the same WordPress installation

## Compatibility

| Component | Minimum Version | Recommended |
|-----------|----------------|-------------|
| **WPsigner** | 2.1.0 | Latest |
| **LearnDash LMS** | 3.0 | 4.0+ |
| **WordPress** | 5.8 | 6.4+ |
| **PHP** | 7.4 | 8.1+ |
| **LearnDash Add-ons** | Compatible with ProPanel, WooCommerce, and Group Management add-ons | -- |

> **tip**
No API keys or external services are needed. The integration communicates entirely through WordPress action hooks and user meta.

---

## Setup

### Step 1: Create a Document Template

1. Go to **WPsigner → Documents → Add New**
2. Create an enrollment agreement document
3. Save it as a **Template** (set status to Template)
4. Note the template — you'll select it in the next step

### Step 2: Configure the Integration

1. Go to **WPsigner → Integrations → LearnDash**
2. Click **Test Connection** to verify LearnDash is detected
3. Configure:

| Setting | Description |
|---|---|
| **Enable** | Turn on the integration |
| **Require Signature** | Before enrollment (blocks access) or before completion (blocks certificate) |
| **Document Template** | Template used for enrollment agreements |
| **Gated Courses** | Select which courses require a signed document |

4. Click **Save Settings**

---

## Gating Modes

### Before Enrollment (Default)

Students cannot access course content until they sign the enrollment agreement. When they visit the course page, they see a **"Sign Agreement"** button.

### Before Completion

Students can access and take the course, but cannot receive their certificate or completion status until they sign.

---

## Technical Details

### Document Creation Flow

1. Student is added to a gated course
2. WPsigner creates a personalized document from the template
3. The document is linked to the course via meta data
4. Student sees a signing prompt on the course page
5. After signing, `user_meta` is updated to grant access

### Data Storage

| Data | Storage |
|---|---|
| Signing status | WordPress `user_meta`: `_wps_learndash_signed_{course_id}` |
| Course link | WPsigner `document_meta`: `_learndash_course_id` |
| Document | Standard WPsigner documents table |

---

## Developer Hooks

WPsigner fires the following action hooks during the LearnDash signing lifecycle. Use them to extend behavior or integrate with third-party systems.

| Hook | Trigger | Parameters |
|------|---------|------------|
| `wps_signer_created` | A signing document is generated for the student | `$signer_id`, `$document_id` |
| `wps_after_document_signed` | The student completes their signature | `$signer_id`, `$document_id` |
| `wps_document_completed` | The enrollment document is fully signed | `$document_id` |

> **tip**
Combine these hooks with LearnDash's own hooks (e.g., `learndash_course_completed`) to build advanced workflows such as auto-enrolling a student in the next course after signing.

```php
add_action( 'wps_document_completed', function( $document_id ) {
    $course_id = get_post_meta( $document_id, '_learndash_course_id', true );
    if ( $course_id ) {
        // Custom logic: e.g., send a welcome email or log the event
    }
});
```

---

## Security

| Feature | Details |
|---|---|
| **No API Keys** | Uses LearnDash's PHP hooks directly |
| **Nonce Verification** | All AJAX requests verified |
| **Rate Limiting** | Test: 5/min, Save: 10/min per user |
| **Capability Check** | Requires `manage_options` for settings |
| **Input Sanitization** | All inputs sanitized with `absint()`, `sanitize_text_field()` |

---

## Troubleshooting

| Issue | Solution |
|---|---|
| "LearnDash Plugin Not Found" | Install and activate LearnDash LMS |
| No courses listed | Publish at least one LearnDash course |
| No templates listed | Create a document with Template status |
| Student still blocked | Verify the signer email matches the WP user email |
| Multiple agreements | Each student/course combo creates one document |

---

## Next Steps

- [WooCommerce Integration](/integrations/woocommerce/) — Require signatures on orders and checkout
- [FluentCRM Integration](/integrations/fluentcrm/) — Sync signers with your CRM for automated follow-ups
- [Gravity Forms Integration](/integrations/gravity-forms/) — Collect signatures through Gravity Forms submissions
- [Contact Form 7 Integration](/integrations/contact-form-7/) — Attach signing workflows to CF7 forms
- [Zapier Integration](/integrations/zapier/) — Connect WPsigner with 5,000+ apps via Zapier
- [n8n Integration](/integrations/n8n/) — Build custom automation workflows with n8n
- [All Integrations](/integrations/) — Browse every available WPsigner integration

---

# Make (Integromat) Integration

> Connect WPsigner with Make to create powerful visual automation scenarios for document signing workflows.

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/make/
Markdown: https://docs.wpsigner.com/md/integrations/make.md

Integrate WPsigner with [Make](https://www.make.com) (formerly Integromat) to automate your document signing workflows using visual drag-and-drop scenarios — no code required.

> **tip**
Make is ideal for non-technical users who want to build complex automation flows with a visual editor. If you prefer code or need self-hosting, check out the [n8n Integration](/integrations/n8n/).

---

## How It Works

WPsigner connects to Make in two directions:

```
Incoming: WPsigner → Webhook → Make Scenario → External App
Outgoing: External App → Make Scenario → WPsigner REST API
```

| Direction | Module in Make | Use Case |
|---|---|---|
| **WPsigner → Make** | Webhooks → Custom Webhook | Trigger scenarios when documents are signed, created, etc. |
| **Make → WPsigner** | HTTP → Make a request | Create documents, send for signing, look up status |

---

## Requirements

- WPsigner **1.3.0+** (webhooks) / **1.8.0+** (REST API)
- A Make account ([Free tier available](https://www.make.com/en/pricing))
- A WPsigner API key with **Full** permissions (for outgoing calls)

---

## Part 1: Receiving WPsigner Events (Triggers)

This enables Make to react when something happens in WPsigner (e.g. a document is signed).

### Step 1: Create a Make Scenario

1. Log in to [Make](https://www.make.com)
2. Click **Create a new scenario**
3. Click the **+** button to add the first module
4. Search for **Webhooks** and select **Custom webhook**

### Step 2: Create a Webhook in Make

1. In the Custom webhook module, click **Add**
2. Give it a name like `WPsigner Events`
3. Click **Save**
4. Make will show you a **webhook URL** — copy it (e.g. `https://hook.make.com/abc123...`)

### Step 3: Register the Webhook in WPsigner

1. Go to **WPsigner → More → Webhooks** in your WordPress admin
2. Click **Add Webhook**
3. Fill in:
   - **Name**: `Make - Document Events`
   - **URL**: Paste the Make webhook URL from Step 2
   - **Events**: Select which events should trigger the scenario (see [Available Events](#available-events) below)
   - **Secret** (optional): Add an HMAC secret for signature verification
4. Click **Save**

### Step 4: Test the Connection

1. Back in Make, click **Run once** on your scenario
2. In WPsigner, create or sign a test document
3. Make should receive the webhook payload and show you the data structure
4. You can now map WPsigner fields to other modules in your scenario

> **note**
After the first test, Make will remember the data structure and show you all available fields for mapping. If Make times out waiting, send a test event from **WPsigner → More → Webhooks → Send test** (if available).

### Step 5: Add Actions to Your Scenario

After the Webhook module, add any Make module to process the data:

- **Google Sheets** → Add Row
- **Slack** → Send Message
- **Gmail** → Send Email
- **Airtable** → Create Record
- **Monday.com** → Create Item

---

## Part 2: Calling WPsigner API (Actions)

This enables Make to create documents, send for signing, and query status in WPsigner.

### Step 1: Generate API Credentials

1. Go to **WPsigner → Settings → API Keys**
2. Click **Generate New Key**
3. Set **Permissions** to `Full`
4. Copy the **API Key** and **API Secret**

> **caution**
Save the API Secret immediately — it is shown only once. If you lose it, you'll need to generate a new key.

### Step 2: Configure HTTP Module in Make

1. In your Make scenario, add the **HTTP** module → **Make a request**
2. Configure as follows:

| Setting | Value |
|---|---|
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents` |
| **Method** | `POST` |
| **Headers** | `X-WPS-API-KEY`: your_key |
| | `X-WPS-API-SECRET`: your_secret |
| | `Content-Type`: `application/json` |
| **Body type** | JSON |

### Step 3: Create a Document

**Request body (JSON):**

```json
{
  "title": "Contract for {{1.name}}"
}
```

**Response:**

```json
{
  "id": 456,
  "title": "Contract for John Smith",
  "status": "draft",
  "created_at": "2026-01-15T10:30:00-05:00"
}
```

### Step 4: Add a Signer

Add another **HTTP** module after the create step:

| Setting | Value |
|---|---|
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents/{{2.id}}/signers` |
| **Method** | `POST` |
| **Body** | `{"name": "{{1.name}}", "email": "{{1.email}}", "role": "signer"}` |

### Step 5: Send for Signing

Add a third **HTTP** module:

| Setting | Value |
|---|---|
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents/{{2.id}}/send` |
| **Method** | `POST` |

---

## Example Scenarios

### Scenario 1: Document Signed → Google Sheets Log

Track every signed document in a spreadsheet for compliance.

```
┌─────────────┐    ┌─────────────┐    ┌──────────────┐
│  WPsigner   │───▶│    Make     │───▶│Google Sheets │
│  Webhook    │    │  Scenario   │    │   Add Row    │
└─────────────┘    └─────────────┘    └──────────────┘
```

**Data mapping:**

| WPsigner Field | Google Sheets Column |
|---|---|
| `data.document.title` | Document Name |
| `data.signer.name` | Signer Name |
| `data.signer.email` | Signer Email |
| `data.signer.signed_at` | Date Signed |
| `timestamp` | Event Timestamp |

---

### Scenario 2: Typeform Response → Create Contract

Auto-generate a signing document when someone fills out a form.

```
┌─────────────┐    ┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  Typeform   │───▶│    HTTP     │───▶│    HTTP     │───▶│    HTTP     │
│  Response   │    │ Create Doc  │    │ Add Signer  │    │ Send Doc   │
└─────────────┘    └─────────────┘    └─────────────┘    └─────────────┘
```

**HTTP module body for Create Document:**

```json
{
  "title": "Contract for {{answers.email}}"
}
```

---

### Scenario 3: Document Completed → Multi-System Update

When all signers finish, update CRM + send invoice.

```
┌──────────┐    ┌────────┐    ┌──────────┐
│ WPsigner │───▶│ Router │───▶│ HubSpot  │
│ Webhook  │    │        │    │  Update  │
│          │    │        │    └──────────┘
│          │    │        │    ┌──────────┐
│          │    │        │───▶│  Stripe  │
│          │    │        │    │ Invoice  │
│          │    │        │    └──────────┘
│          │    │        │    ┌──────────┐
│          │    │        │───▶│  Slack   │
└──────────┘    └────────┘    │  Notify  │
                              └──────────┘
```

Use Make's **Router** module to fan out to multiple actions from a single webhook event.

---

### Scenario 4: Document Declined → Follow-Up Email

When a signer declines, automatically send a personalized follow-up.

**Webhook filter:** Set the Make filter to only process events where `event` = `document.declined`

**Gmail module:**
- **To**: `{{data.signer.email}}`
- **Subject**: `Regarding {{data.document.title}}`
- **Body**: Custom follow-up message

---

## Available Events

Register these events when creating your webhook in WPsigner:

| Event | Triggered When |
|---|---|
| `document.created` | New document is created |
| `document.sent` | Document emails sent to signers |
| `document.viewed` | Signer opens the signing page |
| `document.signed` | Individual signer applies their signature |
| `document.completed` | All signatures complete |
| `document.declined` | Signer declines to sign |
| `document.expired` | Document expires unsigned |
| `signer.reminded` | Reminder sent to a signer |

---

## Webhook Payload Structure

All events send data in this format:

```json
{
  "event": "document.signed",
  "timestamp": "2026-01-15T11:00:00-05:00",
  "data": {
    "document": {
      "id": 123,
      "title": "Service Agreement",
      "status": "sent",
      "created_at": "2026-01-15T10:30:00-05:00"
    },
    "signer": {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "status": "signed",
      "signed_at": "2026-01-15T11:00:00-05:00"
    }
  },
  "meta": {
    "site_url": "https://yourdomain.com",
    "site_name": "Your Site",
    "plugin_version": "1.8.0"
  }
}
```

---

## API Endpoints Reference

These are the WPsigner REST API endpoints you can call from Make's HTTP module:

| Endpoint | Method | Description |
|---|---|---|
| `/wp-json/insigner/v1/documents` | `GET` | List all documents |
| `/wp-json/insigner/v1/documents` | `POST` | Create a new document |
| `/wp-json/insigner/v1/documents/{id}` | `GET` | Get document details |
| `/wp-json/insigner/v1/documents/{id}/signers` | `POST` | Add a signer |
| `/wp-json/insigner/v1/documents/{id}/send` | `POST` | Send for signing |

For the full API reference, see [REST API Documentation](/api/).

---

## Tips & Best Practices

### Error Handling

- Add an **Error handler** module after HTTP requests to catch failures
- Use Make's **Retry** feature for transient errors (network timeouts)
- Log errors to Google Sheets or Slack for monitoring

### Rate Limiting

- WPsigner API keys have configurable rate limits (default: 1000 req/hour)
- Add **Sleep** modules between bulk operations in Make
- If you see `429 Too Many Requests`, increase the rate limit on your API key or reduce scenario frequency

### Filters

Use Make's **Filters** between modules to only process specific events:
- Filter by event type: `event` equals `document.completed`
- Filter by document title: `data.document.title` contains `NDA`
- Filter by signer email: `data.signer.email` ends with `@yourcompany.com`

---

## Troubleshooting

| Issue | Cause | Solution |
|---|---|---|
| Webhook not received in Make | URL incorrect or webhook inactive | Verify the URL in WPsigner → Webhooks and check it's enabled |
| `401 Unauthorized` from API | Invalid API key/secret | Double-check the headers — keys are case-sensitive |
| `403 Forbidden` | Read-only API key | Generate a new key with **Full** permissions |
| `404 Not Found` | Wrong endpoint URL | Verify the URL includes `/wp-json/insigner/v1/` |
| Scenario runs but no data | Event not selected | Check the webhook in WPsigner has the correct events selected |
| Timeout errors | Server slow or blocking | Check hosting — some hosts block outgoing/incoming HTTP requests |

---

## Next Steps

- [n8n Integration](/integrations/n8n/) — Self-hosted automation alternative
- [Zapier Integration](/integrations/zapier/) — Connect with 6,000+ apps via Zapier
- [API Overview](/api/) — Full REST API documentation
- [Google Drive Integration](/integrations/google-drive/) — Auto-backup signed documents

---

# n8n Integration

> Connect WPsigner with n8n for self-hosted workflow automation with full control over your document signing workflows.

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/n8n/
Markdown: https://docs.wpsigner.com/md/integrations/n8n.md

Integrate WPsigner with [n8n](https://n8n.io) for powerful, self-hosted workflow automation. Build complex document signing workflows with n8n's visual editor while keeping all your data on your own infrastructure.

> **tip**
n8n is ideal for technical teams who want full control over their automation. It can be self-hosted for free or used via n8n Cloud. If you prefer a fully hosted solution, check out [Make](/integrations/make/) or [Zapier](/integrations/zapier/).

---

## How It Works

WPsigner connects to n8n in two directions:

```
Incoming: WPsigner → Webhook → n8n Workflow → External App
Outgoing: External App → n8n Workflow → WPsigner REST API
```

| Direction | n8n Node | Use Case |
|---|---|---|
| **WPsigner → n8n** | Webhook node | Trigger workflows when documents are signed, created, etc. |
| **n8n → WPsigner** | HTTP Request node | Create documents, send for signing, look up status |

---

## Requirements

- WPsigner **1.3.0+** (webhooks) / **1.8.0+** (REST API)
- n8n instance — either [self-hosted](https://docs.n8n.io/hosting/) or [n8n Cloud](https://n8n.io/cloud/)
- A WPsigner API key with **Full** permissions (for outgoing calls)

> **note**
Your n8n instance must be publicly accessible for WPsigner webhooks to reach it. If running locally, use a tunnel service like [ngrok](https://ngrok.com) or [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/).

---

## Part 1: Receiving WPsigner Events (Triggers)

This enables n8n to react when something happens in WPsigner.

### Step 1: Add a Webhook Node

1. Open your n8n editor
2. Click **+** to add a new node
3. Search for **Webhook** and select it
4. Configure:
   - **HTTP Method**: `POST`
   - **Path**: Choose a custom path (e.g. `wpsigner-events`)
5. Click **Listen for test event** (or **Execute workflow** in production mode)
6. Copy the **webhook URL** shown (e.g. `https://your-n8n.com/webhook/wpsigner-events`)

> **note**
n8n has two URLs: **Test URL** (for testing) and **Production URL** (for live workflows). Use the production URL when registering in WPsigner.

### Step 2: Register the Webhook in WPsigner

1. Go to **WPsigner → More → Webhooks** in your WordPress admin
2. Click **Add Webhook**
3. Fill in:
   - **Name**: `n8n - Document Events`
   - **URL**: Paste the n8n webhook URL from Step 1 (use the **Production URL**)
   - **Events**: Select which events should trigger the workflow
   - **Secret** (optional): Add an HMAC secret for signature verification
4. Click **Save**

### Step 3: Test the Connection

1. In n8n, click **Listen for test event** on the Webhook node
2. In WPsigner, create or sign a test document
3. n8n should receive the webhook payload and display the data structure
4. You can now add more nodes to process the data

### Step 4: Add Processing Nodes

After the Webhook node, add any n8n node to process the data:

**Common nodes:**
- **IF** — Filter by event type
- **Set** — Transform/rename fields
- **Slack** — Send notifications
- **Google Sheets** — Log to spreadsheet
- **HTTP Request** — Call external APIs
- **Email (SMTP)** — Send custom emails
- **Postgres / MySQL** — Write to database

---

## Part 2: Calling WPsigner API (Actions)

This enables n8n to create documents, send for signing, and query status.

### Step 1: Generate API Credentials

1. Go to **WPsigner → Settings → API Keys**
2. Click **Generate New Key**
3. Set **Permissions** to `Full`
4. Copy the **API Key** and **API Secret**

> **caution**
Save the API Secret immediately — it is shown only once. If you lose it, generate a new key.

### Step 2: Store Credentials in n8n

For reusability, store your WPsigner credentials in n8n:

1. Go to **Settings → Credentials** in n8n
2. Click **Add Credential** → **Header Auth**
3. Add two header parameters:
   - **Name**: `X-WPS-API-KEY` → **Value**: your API key
   - **Name**: `X-WPS-API-SECRET` → **Value**: your API secret
4. Save as `WPsigner API`

Now you can reuse this credential in all HTTP Request nodes.

### Step 3: Create a Document

Add an **HTTP Request** node:

| Setting | Value |
|---|---|
| **Method** | `POST` |
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents` |
| **Authentication** | Header Auth → `WPsigner API` |
| **Body Content Type** | JSON |
| **Body** | See below |

```json
{
  "title": "{{ $json.deal_name }} Agreement"
}
```

### Step 4: Add a Signer

Add another **HTTP Request** node:

| Setting | Value |
|---|---|
| **Method** | `POST` |
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents/{{ $json.id }}/signers` |
| **Body** | See below |

```json
{
  "name": "{{ $json.contact_name }}",
  "email": "{{ $json.contact_email }}",
  "role": "signer"
}
```

### Step 5: Send for Signing

Add a third **HTTP Request** node:

| Setting | Value |
|---|---|
| **Method** | `POST` |
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents/{{ $node['Create Document'].json.id }}/send` |

---

## Example Workflows

### Workflow 1: Document Signed → Slack + Google Sheets

Log signed documents and notify your team simultaneously.

```
┌──────────┐    ┌────────┐    ┌──────────┐
│ Webhook  │───▶│   IF   │───▶│  Slack   │
│ WPsigner │    │ event= │    │  Notify  │
│          │    │ signed │    └──────────┘
│          │    │        │    ┌──────────┐
│          │    │        │───▶│  Sheets  │
└──────────┘    └────────┘    │  Log Row │
                              └──────────┘
```

**IF node condition:** `{{ $json.event }}` equals `document.signed`

**Slack message:**
```
📝 Document "{{ $json.data.document.title }}" signed by {{ $json.data.signer.name }} ({{ $json.data.signer.email }})
```

---

### Workflow 2: CRM Deal → Auto-Create Contract

When a deal closes in your CRM, automatically generate and send a contract.

```
┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│ CRM      │───▶│ Create   │───▶│  Add     │───▶│  Send    │
│ Trigger  │    │ Document │    │ Signer   │    │ Document │
└──────────┘    └──────────┘    └──────────┘    └──────────┘
```

---

### Workflow 3: Document Completed → Database + Cloud Storage

When all signers finish, archive the record.

```
┌──────────┐    ┌────────┐    ┌──────────┐
│ Webhook  │───▶│  IF    │───▶│ Postgres │
│ WPsigner │    │ event= │    │  INSERT  │
│          │    │ compl. │    └──────────┘
│          │    │        │    ┌──────────┐
│          │    │        │───▶│   S3     │
└──────────┘    └────────┘    │  Upload  │
                              └──────────┘
```

---

### Workflow 4: Scheduled Document Status Check

Run daily to find unsigned documents and send reminders.

```
┌──────────┐    ┌──────────┐    ┌────────┐    ┌──────────┐
│  Cron    │───▶│  HTTP   │───▶│   IF   │───▶│  Email   │
│  Daily   │    │  GET    │    │ status │    │ Reminder │
│  9:00AM  │    │  /docs  │    │ =sent  │    │          │
└──────────┘    └──────────┘    └────────┘    └──────────┘
```

**HTTP Request:**
- `GET https://yoursite.com/wp-json/insigner/v1/documents?status=sent`

**IF condition:** Filter documents older than 3 days

---

## Available Events

Register these events when creating your webhook in WPsigner:

| Event | Triggered When |
|---|---|
| `document.created` | New document is created |
| `document.sent` | Document emails sent to signers |
| `document.viewed` | Signer opens the signing page |
| `document.signed` | Individual signer applies their signature |
| `document.completed` | All signatures complete |
| `document.declined` | Signer declines to sign |
| `document.expired` | Document expires unsigned |
| `signer.reminded` | Reminder sent to a signer |

---

## Webhook Payload Structure

All events send data in this format:

```json
{
  "event": "document.signed",
  "timestamp": "2026-01-15T11:00:00-05:00",
  "data": {
    "document": {
      "id": 123,
      "title": "Service Agreement",
      "status": "sent",
      "created_at": "2026-01-15T10:30:00-05:00"
    },
    "signer": {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "status": "signed",
      "signed_at": "2026-01-15T11:00:00-05:00"
    }
  },
  "meta": {
    "site_url": "https://yourdomain.com",
    "site_name": "Your Site",
    "plugin_version": "1.8.0"
  }
}
```

---

## API Endpoints Reference

These are the WPsigner REST API endpoints you can call from n8n's HTTP Request node:

| Endpoint | Method | Description |
|---|---|---|
| `/wp-json/insigner/v1/documents` | `GET` | List all documents |
| `/wp-json/insigner/v1/documents` | `POST` | Create a new document |
| `/wp-json/insigner/v1/documents/{id}` | `GET` | Get document details |
| `/wp-json/insigner/v1/documents/{id}/signers` | `POST` | Add a signer |
| `/wp-json/insigner/v1/documents/{id}/send` | `POST` | Send for signing |

For the full API reference, see [REST API Documentation](/api/).

---

## Tips & Best Practices

### Credential Management

- Store WPsigner API credentials as **Header Auth** credentials in n8n for reuse
- Never hardcode secrets in workflow expressions
- Use separate API keys for production vs. testing workflows

### Error Handling

- Add **Error Trigger** nodes to catch and log failures
- Use n8n's built-in **retry on fail** option on HTTP Request nodes
- Set up a Slack/email notification for failed workflow executions

### Performance

- WPsigner API keys have configurable rate limits (default: 1000 req/hour)
- Use n8n's **Wait** node between bulk operations
- If you see `429 Too Many Requests`, increase the rate limit on your API key
- For high-volume workflows, consider using n8n's queue mode

### Testing

1. Use n8n's **test URL** during development
2. Switch to the **production URL** when activating the workflow
3. Test with a sample document before going live
4. Monitor the first few executions for data mapping issues

---

## Troubleshooting

| Issue | Cause | Solution |
|---|---|---|
| Webhook not received | URL incorrect or workflow inactive | Verify the URL and ensure workflow is **active** (not just saved) |
| n8n shows "Waiting for webhook" | WPsigner hasn't sent an event yet | Trigger an event (create/sign a document) or check webhook URL |
| `401 Unauthorized` from API | Invalid API key or secret | Double-check `X-WPS-API-KEY` and `X-WPS-API-SECRET` headers |
| `403 Forbidden` | Read-only API key | Generate a new key with **Full** permissions |
| `404 Not Found` | Wrong endpoint URL | Verify the URL includes `/wp-json/insigner/v1/` |
| Expressions not resolving | Wrong n8n expression syntax | Use `{{ $json.field }}` for current node data |
| Webhook data incomplete | Not all events selected | Check WPsigner → Webhooks and enable the needed events |

---

## Self-Hosting Tips

If you're self-hosting n8n:

- Ensure your n8n instance has a **public URL** or use a tunnel (ngrok, Cloudflare Tunnel)
- Set `WEBHOOK_URL` environment variable to your public URL
- Use `N8N_PROTOCOL=https` for secure webhook delivery
- Consider using Docker with a reverse proxy (nginx/Caddy) for production

---

## Next Steps

- [Make Integration](/integrations/make/) — Hosted visual automation alternative
- [Zapier Integration](/integrations/zapier/) — Connect with 6,000+ apps
- [API Overview](/api/) — Full REST API documentation
- [Google Drive Integration](/integrations/google-drive/) — Auto-backup signed documents

---

# OneDrive

> Back up signed PDFs to Microsoft OneDrive

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/onedrive/
Markdown: https://docs.wpsigner.com/md/integrations/onedrive.md

Automatically upload signed PDFs to **Microsoft OneDrive** when all signatures are complete.

---

## Requirements

- WPsigner **2.1.0+**
- A Microsoft account (personal, work, or school)
- An Azure App Registration with Graph API permissions

---

## Setup

### Step 1: Register an Azure App

1. Go to [Azure App Registrations](https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade)
2. Click **New registration**
3. Name: "WPsigner Backup"
4. Supported account types: **Accounts in any organizational directory and personal Microsoft accounts**
5. Redirect URI (Web):

```
https://your-site.com/wp-admin/admin.php?page=wpsigner-onedrive
```

6. Click **Register**
7. Copy the **Application (client) ID**

### Step 2: Create Client Secret

1. Go to **Certificates & secrets → New client secret**
2. Description: "WPsigner"
3. Expiration: Choose duration
4. Copy the **Value** (not the ID)

> **important**
Copy the client secret value immediately after creation. Azure only shows it once. If you lose it, you must create a new secret.

### Step 3: Add API Permissions

1. Go to **API permissions → Add a permission**
2. Choose **Microsoft Graph → Delegated permissions**
3. Add: `Files.ReadWrite.All`, `User.Read`, `offline_access`
4. Click **Grant admin consent** if required

### Step 4: Configure WPsigner

1. Go to **WPsigner → Integrations → OneDrive**
2. Enter your Application ID and Client Secret
3. Click **Save Settings**
4. Click **Authorize OneDrive**
5. Sign in and approve permissions
6. You'll be redirected back showing **Connected**

---

## How It Works

When all signers complete their signatures, WPsigner uploads the signed PDF to OneDrive via the **Microsoft Graph API**. The upload method is selected automatically based on file size:

| File Size | Upload Method | Details |
|-----------|--------------|---------|
| < 4 MB | Simple PUT upload | Single HTTP request to `/me/drive/root:/{path}:/content` |
| ≥ 4 MB | Chunked upload session | Creates an upload session, then streams in **10 MB chunks** |

Files are uploaded to `OneDrive:/WPsigner/Title_Date_ID.pdf`.

### Chunked Upload Process

For files 4 MB or larger, WPsigner uses the Microsoft Graph upload session API:

1. **Create session** — `POST` to `/createUploadSession` with conflict behavior set to `rename`
2. **Stream chunks** — Sequential `PUT` requests with `Content-Range` headers, each carrying up to 10 MB
3. **Finalize** — The last chunk response confirms the file creation
4. **Cleanup** — Local file handle is closed; audit trail entry is logged

> **tip**
Chunked uploads support files of any size and are resilient to individual chunk failures. The 10 MB chunk size is optimized for WordPress HTTP timeout limits.

---

## File Naming

The default file naming pattern is:

```
{folder}/{sanitized_title}_{date}_{document_id}.pdf
```

| Segment | Example | Description |
|---------|---------|-------------|
| `{folder}` | `WPsigner` | Configurable in settings (default: `WPsigner`) |
| `{sanitized_title}` | `Service-Agreement` | Document title, sanitized for safe file names |
| `{date}` | `2026-03-06` | Signing date in `Y-m-d` format |
| `{document_id}` | `142` | Internal WPsigner document ID |

**Full path example:**

```
WPsigner/Service-Agreement_2026-03-06_142.pdf
```

You can change the destination folder in **WPsigner → Integrations → OneDrive**. OneDrive creates the folder automatically if it doesn't exist.

### Custom Paths

Use the `wps_onedrive_backup_path` filter to customize the path dynamically:

```php
add_filter('wps_onedrive_backup_path', function ($onedrive_path, $document_id, $document) {
    $year = wp_date('Y');
    return "WPsigner/{$year}/" . basename($onedrive_path);
}, 10, 3);
```

---

## Use Cases

| Scenario | Configuration |
|----------|---------------|
| Personal document archive | Connect with a personal Microsoft account |
| Corporate compliance storage | Use a work/school account with SharePoint-backed OneDrive |
| Team-accessible contracts | Set the folder to a shared OneDrive directory |
| Multi-cloud backup | Pair with [Dropbox](/integrations/dropbox/) or [Amazon S3](/integrations/amazon-s3/) |
| Large document support | Chunked upload handles PDFs of any size automatically |

---

## Compatibility

| Component | Supported Versions |
|-----------|--------------------|
| **WPsigner** | 2.1.0+ |
| **WordPress** | 6.0+ |
| **PHP** | 7.4+ |
| **Microsoft Graph API** | v1.0 |
| **Microsoft Accounts** | Personal, Work, School |
| **OneDrive Plans** | All plans (Free, Microsoft 365, OneDrive for Business) |
| **Max file size** | Unlimited (chunked upload for files ≥ 4 MB) |
| **Multisite** | Supported (per-site configuration) |

---

## Security

| Feature | Details |
|---------|---------|
| **Microsoft Identity** | OAuth 2.0 via `login.microsoftonline.com` |
| **AES-256-GCM** | Client secret and tokens encrypted at rest |
| **Token Refresh** | Automatic refresh via `offline_access` scope |
| **Rate Limiting** | Test: 5/min, Save: 10/min per user |

---

## Troubleshooting

| Issue | Cause | Solution |
|-------|-------|----------|
| "Not connected. Please authorize first." | OAuth tokens are missing or revoked | Click **Authorize OneDrive** to reconnect |
| "Security check failed" | Nonce expired | Refresh the page and retry |
| "Too many requests" | Rate limit exceeded | Wait 60 seconds and retry |
| Token refresh fails silently | Client secret expired in Azure | Create a new client secret in Azure, update it in WPsigner, and re-authorize |
| `AADSTS700016` error during auth | Application ID is incorrect | Verify the Application (client) ID matches your Azure registration |
| `AADSTS65001` — consent required | Admin consent not granted | Ask your Azure AD admin to grant consent for the app permissions |
| "Upload failed" with 403 | Insufficient Graph API permissions | Ensure `Files.ReadWrite.All` is granted and consented |
| "Failed to create upload session" | OneDrive storage is full | Free up space or upgrade the OneDrive plan |
| Files appear in wrong location | Folder name was changed after authorization | Verify the folder setting in WPsigner matches your intended path |
| Chunked upload hangs | Server timeout too low | Increase PHP `max_execution_time` (recommended: 300+ for large files) |

> **caution**
Azure client secrets have an expiration date. When a secret expires, WPsigner can no longer refresh tokens and uploads will fail silently. Set a calendar reminder to rotate the secret before it expires.

---

## Developer Hooks

### `wps_onedrive_backup_path`

Filter the full OneDrive path before upload.

**Parameters:**

| Parameter | Type | Description |
|-----------|------|-------------|
| `$onedrive_path` | `string` | Full OneDrive path (e.g., `WPsigner/Title_2026-03-06_42.pdf`) |
| `$document_id` | `int` | WPsigner document ID |
| `$document` | `object` | Document object |

### `wps_onedrive_uploaded`

Action fired after a successful upload.

```php
add_action('wps_onedrive_uploaded', function ($document_id, $onedrive_path) {
    error_log("Document {$document_id} backed up to OneDrive: {$onedrive_path}");
}, 10, 2);
```

---

## Next Steps

- [Dropbox](/integrations/dropbox/) — Dropbox backup
- [Google Drive](/integrations/google-drive/) — Google Drive backup
- [Amazon S3](/integrations/amazon-s3/) — S3 bucket storage
- [Cloudflare R2](/integrations/cloudflare-r2/) — Zero egress, global CDN
- [Wasabi](/integrations/wasabi/) — S3-compatible, no egress fees

---

# Pabbly Connect

> Connect WPsigner with Pabbly Connect for affordable, no-code workflow automation

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/pabbly/
Markdown: https://docs.wpsigner.com/md/integrations/pabbly.md

Integrate WPsigner with [Pabbly Connect](https://www.pabbly.com/connect/) to automate your document signing workflows — no coding required. Pabbly offers unlimited workflows with a one-time pricing option.

> **tip**
Compare current plan limits and pricing directly with each provider. If you prefer other visual automation options, see [Make](/integrations/make/) or [Zapier](/integrations/zapier/).

---

## How It Works

WPsigner connects to Pabbly Connect in two directions:

```
Incoming: WPsigner → Webhook → Pabbly Workflow → External App
Outgoing: External App → Pabbly Workflow → WPsigner REST API
```

| Direction | Pabbly Module | Use Case |
|---|---|---|
| **WPsigner → Pabbly** | Webhook trigger | React when documents are signed, created, etc. |
| **Pabbly → WPsigner** | API Request (Custom) | Create documents, send for signing |

---

## Requirements

- WPsigner **1.3.0+** (webhooks) / **1.8.0+** (REST API)
- A [Pabbly Connect](https://www.pabbly.com/connect/) account
- A WPsigner API key with **Full** permissions (for outgoing calls)

---

## Part 1: Receiving WPsigner Events (Triggers)

### Step 1: Create a Pabbly Workflow

1. Log in to [Pabbly Connect](https://connect.pabbly.com)
2. Click **Create Workflow**
3. Name it (e.g., `WPsigner Document Events`)

### Step 2: Set Up Webhook Trigger

1. For the **Trigger** app, select **Webhook / API** → **Catch Hook**
2. Pabbly will generate a unique **Webhook URL** — copy it
3. Click **Capture Response** to wait for the first event

### Step 3: Register the Webhook in WPsigner

1. Go to **WPsigner → More → Webhooks** in your WordPress admin
2. Click **Add Webhook**
3. Fill in:
   - **Name**: `Pabbly - Document Events`
   - **URL**: Paste the Pabbly webhook URL
   - **Events**: Select the events you want (see [Available Events](#available-events))
   - **Secret** (optional): Add for HMAC signature verification
4. Click **Save**

### Step 4: Test the Connection

1. In WPsigner, create or sign a test document
2. In Pabbly, you should see the captured payload
3. Click **Save & Send Test Response**

### Step 5: Add Action Steps

After the trigger, add action modules:

- **Google Sheets** → Add Row
- **Gmail** → Send Email
- **Slack** → Send Message
- **Notion** → Create Page
- **Discord** → Send Message

---

## Part 2: Calling WPsigner API (Actions)

### Step 1: Generate API Credentials

1. Go to **WPsigner → Settings → API Keys**
2. Click **Generate New Key**
3. Set **Permissions** to `Full`
4. Copy the **API Key** and **API Secret**

> **caution**
Save the API Secret immediately — it is shown only once.

### Step 2: Add API Request Action in Pabbly

1. For the **Action** app, select **API Request (Custom)**
2. Configure:

| Setting | Value |
|---|---|
| **Method** | `POST` |
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents` |
| **Headers** | `X-WPS-API-KEY`: your_key |
| | `X-WPS-API-SECRET`: your_secret |
| | `Content-Type`: `application/json` |
| **Body** | See below |

### Step 3: Create a Document

```json
{
  "title": "Contract for {{step1.name}}"
}
```

### Step 4: Add a Signer

Add another **API Request (Custom)** action:

| Setting | Value |
|---|---|
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents/{{step2.id}}/signers` |
| **Body** | `{"name": "{{step1.name}}", "email": "{{step1.email}}", "role": "signer"}` |

### Step 5: Send for Signing

Add a third action:

| Setting | Value |
|---|---|
| **URL** | `https://yoursite.com/wp-json/insigner/v1/documents/{{step2.id}}/send` |
| **Method** | `POST` |

---

## Example Workflows

### Workflow 1: Document Signed → Google Sheets Log

```
┌─────────────┐    ┌─────────────┐    ┌──────────────┐
│  WPsigner   │───▶│   Pabbly    │───▶│Google Sheets │
│  Webhook    │    │  Workflow   │    │   Add Row    │
└─────────────┘    └─────────────┘    └──────────────┘
```

### Workflow 2: Form Submission → Auto Contract

```
┌─────────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│  Typeform   │───▶│ Create   │───▶│  Add     │───▶│  Send    │
│  Submission │    │ Document │    │ Signer   │    │ for Sign │
└─────────────┘    └──────────┘    └──────────┘    └──────────┘
```

### Workflow 3: All Signed → CRM + Notification

```
┌──────────┐    ┌──────────┐    ┌──────────┐
│ WPsigner │───▶│   CRM    │    │  Slack   │
│ Complete │    │  Update  │    │  Notify  │
│          │    └──────────┘    └──────────┘
└──────────┘
```

---

## Available Events

| Event | Triggered When |
|---|---|
| `document.created` | New document is created |
| `document.sent` | Document emails sent to signers |
| `document.viewed` | Signer opens the signing page |
| `document.signed` | Individual signer applies their signature |
| `document.completed` | All signatures complete |
| `document.declined` | Signer declines to sign |
| `document.expired` | Document expires unsigned |
| `signer.reminded` | Reminder sent to a signer |

---

## Webhook Payload Structure

```json
{
  "event": "document.signed",
  "timestamp": "2026-01-15T11:00:00-05:00",
  "data": {
    "document": {
      "id": 123,
      "title": "Service Agreement",
      "status": "sent"
    },
    "signer": {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "status": "signed",
      "signed_at": "2026-01-15T11:00:00-05:00"
    }
  },
  "meta": {
    "site_url": "https://yourdomain.com",
    "plugin_version": "2.1.0"
  }
}
```

---

## API Endpoints Reference

| Endpoint | Method | Description |
|---|---|---|
| `/wp-json/insigner/v1/documents` | `GET` | List all documents |
| `/wp-json/insigner/v1/documents` | `POST` | Create a new document |
| `/wp-json/insigner/v1/documents/{id}` | `GET` | Get document details |
| `/wp-json/insigner/v1/documents/{id}/signers` | `POST` | Add a signer |
| `/wp-json/insigner/v1/documents/{id}/send` | `POST` | Send for signing |

For the full API reference, see [REST API Documentation](/api/).

---

## Pabbly vs Make vs Zapier

| Feature | Pabbly Connect | Make | Zapier |
|---------|---------------|------|--------|
| Pricing | One-time or subscription | Per operation | Per task |
| Task limits | Unlimited (paid) | Based on plan | Based on plan |
| Visual editor | ✅ | ✅ | ✅ |
| Multi-step | ✅ | ✅ | ✅ |
| Webhook trigger | ✅ | ✅ | ✅ |
| API Request action | ✅ | ✅ | Via Code |
| Delay/Wait | ✅ | ✅ | ✅ |

---

## Troubleshooting

| Issue | Cause | Solution |
|---|---|---|
| Webhook not captured | URL incorrect | Re-copy the webhook URL from Pabbly |
| `401` from WPsigner API | Invalid credentials | Verify `X-WPS-API-KEY` and `X-WPS-API-SECRET` |
| `403` error | Read-only key | Generate a key with **Full** permissions |
| Response empty | Events not selected | Enable the correct events in WPsigner → Webhooks |
| Delay in triggers | Pabbly webhook polling | Webhooks are instant; check Pabbly workflow is active |

---

## Next Steps

- [Make Integration](/integrations/make/) — Visual automation with per-operation pricing
- [n8n Integration](/integrations/n8n/) — Self-hosted automation
- [Zapier Integration](/integrations/zapier/) — 6,000+ app integrations
- [API Overview](/api/) — Full REST API documentation

---

# Pipedrive

> Sync document signing events with Pipedrive CRM

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/pipedrive/
Markdown: https://docs.wpsigner.com/md/integrations/pipedrive.md

Automatically sync signing events with **Pipedrive CRM** — find or create persons, log activities, and add notes when documents are signed.

---

## Requirements

- WPsigner **2.1.0+**
- A [Pipedrive](https://www.pipedrive.com/) account
- Your Pipedrive API Token

---

## Setup

### Step 1: Get Your API Token

1. Go to **Pipedrive → Settings → Personal preferences → API**
2. Copy your **API Token**

### Step 2: Configure WPsigner

1. Go to **WPsigner → Integrations → Pipedrive**
2. Paste your API Token
3. Enter your company domain (e.g., "yourcompany")
4. Toggle desired sync options:
   - **Create Person** — auto-create if email not found
   - **Create Activity** — log signing as completed task
   - **Create Note** — add signing details to person
5. Click **Save Settings**
6. Click **Test Connection** to verify

---

## What Happens When a Document Is Signed

| Step | Action |
|------|--------|
| 1 | Search Pipedrive for person by signer's email |
| 2 | Create person if not found (if enabled) |
| 3 | Create activity: "Document Signed: Title" |
| 4 | Create note: document details, signer, date |
| 5 | Log to WPsigner audit trail |

This process runs for **each signer** on the document. If a document has three signers, WPsigner creates activities and notes for all three persons independently.

### Activity Details

When **Create Activity** is enabled, WPsigner creates a Pipedrive activity with the following properties:

| Field | Value |
|-------|-------|
| **Subject** | `Document Signed: {document title}` |
| **Type** | Task |
| **Status** | Done (marked as completed) |
| **Due date** | Current date |
| **Due time** | Current time |
| **Note** | HTML block with document title, signer name, email, and signing date |
| **Person** | Linked to the matched or newly created person |

### Note Details

When **Create Note** is enabled, WPsigner attaches an HTML note to the person record containing:

| Field | Value |
|-------|-------|
| **Document** | Document title |
| **Date** | Full signing date and time (e.g., "March 6, 2026 2:30 PM") |
| **Document ID** | Internal WPsigner document ID |

> **tip**
Activities and notes appear on the person's timeline in Pipedrive, giving your sales team immediate visibility into the signing status without leaving the CRM.

---

## Custom Fields

You can extend the data WPsigner sends to Pipedrive by hooking into the sync process with the `wps_pipedrive_synced` action. While WPsigner does not natively map custom fields, you can use this hook to update person records with custom field values via the Pipedrive API.

**Example: Add a "Last Document Signed" custom field to the person:**

```php
add_action('wps_pipedrive_synced', function ($document_id, $document, $signers) {
    $pipedrive = WPS_Pipedrive::get_instance();

    foreach ($signers as $signer) {
        $person_id = $pipedrive->find_person_by_email($signer->email);
        if (!$person_id) continue;

        // Replace 'abc123' with your Pipedrive custom field key
        $pipedrive->api_request('PUT', "/persons/{$person_id}", [
            'abc123' => $document->title,
        ]);
    }
}, 10, 3);
```

> **note**
To find your custom field key, go to **Pipedrive → Settings → Data fields → Person** and check the API key column.

---

## Use Cases

| Scenario | Configuration |
|----------|---------------|
| Sales teams tracking signed proposals | Enable all three sync options |
| Legal departments logging contract completions | Enable Activity + Note, disable Create Person |
| Onboarding workflows — auto-register new signers | Enable Create Person + Note |
| Audit-only — record events without CRM clutter | Disable Pipedrive sync, rely on WPsigner audit trail |
| Multi-signer contracts (e.g., both parties) | All signers are synced individually to their person records |

---

## Compatibility

| Component | Supported Versions |
|-----------|--------------------|
| **WPsigner** | 2.1.0+ |
| **WordPress** | 6.0+ |
| **PHP** | 7.4+ |
| **Pipedrive API** | v1 (REST) |
| **Pipedrive Plans** | All plans (Essential, Advanced, Professional, Enterprise) |
| **Multisite** | Supported (per-site configuration) |

---

## Security

| Feature | Details |
|---------|---------|
| **API Token** | Encrypted with AES-256-GCM at rest |
| **Rate Limiting** | Test: 5/min, Save: 10/min per user |
| **Capability Check** | `manage_options` required |
| **Nonce Verification** | All AJAX requests verified |

---

## Troubleshooting

| Issue | Cause | Solution |
|-------|-------|----------|
| "Security check failed" | Nonce expired or invalid session | Refresh the page and try again |
| "Too many requests" | Rate limit exceeded (5 test / 10 save per minute) | Wait 60 seconds and retry |
| "Permission denied" | Current user lacks `manage_options` | Log in as an Administrator |
| Test succeeds but no sync happens | Integration enabled but sync options are all off | Toggle at least one sync option (Create Activity, Create Note) |
| Person not found and not created | "Create Person" is disabled | Enable "Create Person" in sync options |
| Activities not showing in Pipedrive | Signer email is empty or missing | Ensure all signers have valid email addresses |
| Incorrect company domain | Domain format is wrong | Enter just the subdomain (e.g., `yourcompany`), not the full URL |
| Connection test fails with "HTTP 401" | Invalid or expired API token | Regenerate the token in Pipedrive settings and paste the new one |
| Duplicate persons created | Multiple persons share the same email | Merge duplicates in Pipedrive; WPsigner matches by first email result |

> **caution**
Pipedrive API tokens do not expire, but they are revoked when you change your password or an admin removes your account. If sync stops working, re-check your token.

---

## Next Steps

- [HubSpot](/integrations/hubspot/) — HubSpot CRM sync
- [Zapier](/integrations/zapier/) — Connect to 5000+ apps
- [Webhooks](/api/webhooks/) — Send signing events to any endpoint
- [Slack](/integrations/slack/) — Get notified in Slack channels
- [Audit Trail](/core-features/) — Review all document events

---

# Slack

> Send signing notifications to a Slack channel

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/slack/
Markdown: https://docs.wpsigner.com/md/integrations/slack.md

## Overview

The Slack integration sends document signing notifications to a Slack channel using [Incoming Webhooks](https://api.slack.com/messaging/webhooks). When a document requires signatures, is signed, or is completed, a rich notification with action buttons appears in your designated Slack channel.

> **note**
Requires WPsigner 2.1.0 or later.

## Use Cases

| Scenario | Description |
|----------|-------------|
| **Team awareness** | Keep your team informed of every signing event without leaving Slack |
| **Sales deal closing** | Notify the sales channel the instant a proposal or contract is signed |
| **HR document flow** | Alert HR when new-hire paperwork (NDAs, offer letters) is executed |
| **Legal review** | Post signing activity to a legal channel for real-time compliance monitoring |
| **Executive visibility** | Surface document completion status to leadership channels |
| **Fast follow-up** | Use action buttons in Slack messages to jump straight to the document |

## Prerequisites

- A Slack workspace where you have admin permissions
- WPsigner 2.1.0+ installed and activated

## Compatibility

| Component | Minimum Version | Recommended |
|-----------|----------------|-------------|
| **WPsigner** | 2.1.0 | Latest |
| **WordPress** | 5.8 | 6.4+ |
| **PHP** | 7.4 | 8.1+ |
| **Slack API** | Incoming Webhooks v2 | Incoming Webhooks v2 |
| **TLS** | 1.2 | 1.3 |
| **PHP Extensions** | `curl`, `json` | `curl`, `json`, `openssl` |

## Setup

1. **Create a Slack App**

   Go to [api.slack.com/apps](https://api.slack.com/apps) and click **Create New App** → **From scratch**.

   Name it something like "WPsigner" and select your workspace.

2. **Enable Incoming Webhooks**

   In the app settings sidebar, click **Incoming Webhooks** and toggle it **ON**.

3. **Add a Webhook**

   Click **Add New Webhook to Workspace**, select the channel where you want to receive notifications, and click **Allow**.

4. **Copy the Webhook URL**

   Copy the generated webhook URL. It will look like:

   ```
   https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX
   ```

5. **Configure in WPsigner**

   Go to **WPsigner** → **Integrations** → **Slack** → **Configure**.

   - Toggle **Enable Slack Notifications**
   - Paste your webhook URL
   - Optionally enter the channel name for your reference
   - Click **Test Connection** to verify
   - Click **Save Settings**

## Notifications

The integration sends rich [Block Kit](https://api.slack.com/block-kit) messages for four events:

### Document Ready for Signature

When a document is created and a signer is assigned, a notification with a **Sign Document** button is sent to the channel.

### Signature Recorded

When a signer completes their signature, a confirmation notice appears in the channel.

### Document Completed

When all signers have signed, a completion notification is sent with the total signer count.

### Reminder (Manual)

When an admin sends a manual reminder, a notification with a **Sign Now** button appears.

## Security

| Measure | Implementation |
|---------|---------------|
| Webhook URL storage | Encrypted at rest (AES-256-GCM) |
| URL validation | Domain restricted to `hooks.slack.com` only |
| URL privacy | Never echoed back to the browser |
| Transport security | HTTPS + TLS 1.2+ (enforced by Slack) |
| Access control | WordPress admin capability check |
| CSRF protection | WordPress nonce verification |
| Content escaping | All user content escaped before inclusion |

## Troubleshooting

### "Invalid Slack webhook URL"

The URL must start with `https://hooks.slack.com/`. Verify you copied the full URL from Slack.

### "Test failed" with `channel_not_found`

The channel associated with the webhook may have been deleted or archived. Create a new webhook for an active channel.

### "Test failed" with `invalid_payload`

Ensure your WPsigner installation uses PHP 7.4+ with `json_encode` support.

### No message appears after test

1. Check that the webhook is enabled in your Slack App settings
2. Verify the channel hasn't been archived
3. Check that your server can reach `hooks.slack.com` (no firewall blocking outbound HTTPS)

## API Reference

### Hooks Used

| Hook | Event |
|------|-------|
| `wps_signer_created` | Signing request sent |
| `wps_after_document_signed` | Signature recorded |
| `wps_document_completed` | All signatures complete |

### Options

| Option Key | Description |
|-----------|-------------|
| `wps_slack_enabled` | Integration enabled (boolean) |
| `wps_slack_webhook_url` | Encrypted webhook URL |
| `wps_slack_channel_name` | Display channel name (string) |

## Comparison

| Feature | Slack | Telegram | WhatsApp |
|---------|-------|----------|----------|
| **Target** | Channel (team) | Individual (Chat ID) | Individual (phone) |
| **Auth** | Webhook URL | Bot Token | API Token |
| **Message format** | Block Kit (mrkdwn) | HTML | Template |
| **Setup complexity** | Low | Medium | High |
| **Rate limit** | 1/sec/webhook | 30/sec | Varies |

## Next Steps

- [Microsoft Teams Integration](/integrations/teams/) — Receive Adaptive Card notifications in Teams channels
- [Telegram Integration](/integrations/telegram/) — Notify individual signers via a Telegram bot
- [Twilio SMS Integration](/integrations/twilio/) — Deliver signing requests and reminders by SMS
- [WhatsApp Integration](/integrations/whatsapp/) — Send notifications via WhatsApp Business API
- [Zapier Integration](/integrations/zapier/) — Connect WPsigner with 5,000+ apps via Zapier
- [n8n Integration](/integrations/n8n/) — Build custom automation workflows with n8n
- [Make Integration](/integrations/make/) — Create visual automation scenarios with Make
- [All Integrations](/integrations/) — Browse every available WPsigner integration

---

# Stripe Payments

> Configure Stripe to collect payments during document signing and in Smart Signing Forms payment fields.

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/stripe/
Markdown: https://docs.wpsigner.com/md/integrations/stripe.md

Collect payments with **Stripe** inside WPsigner. The same Stripe configuration powers:

- **Payment fields** on the document signing page (signers pay before completing)
- **Payment fields** in [Smart Signing Forms](/addons/smart-signing-forms/) (addon)

> **note**
Stripe must be **enabled** under **WPsigner → Integrations** before you open the Stripe settings page.

---

## Prerequisites

- **WPsigner** v3.0.0 or later (Stripe integration v3.21+)
- A [Stripe account](https://dashboard.stripe.com/register)
- **HTTPS** on your WordPress site (required for live payments and webhooks)
- For Smart Forms payments: [Smart Signing Forms](/addons/smart-signing-forms/) installed and activated

---

## Enable Stripe

1. Go to **WPsigner → Integrations**
2. Find the **Stripe** card
3. Toggle the integration **On**
4. Click **Configure** to open Stripe settings

---

## Environment Mode

WPsigner stores **separate keys** for test and live mode.

| Mode | Use when |
|------|----------|
| **Test Mode** | Setting up, testing cards, staging sites |
| **Live Mode** | Real customer payments on production |

Switch mode with the **Test Mode / Live Mode** toggle at the top of the Stripe settings page. Only the keys for the active mode are used for payments and webhooks.

> **tip**
Use [Stripe test cards](https://docs.stripe.com/testing) (for example `4242 4242 4242 4242`) while Test Mode is active.

---

## API Keys

Get keys from the [Stripe Dashboard](https://dashboard.stripe.com/apikeys) (use the **test** dashboard when in Test Mode).

| Field | Description |
|-------|-------------|
| **Publishable Key** | Starts with `pk_test_` or `pk_live_`. Safe to use in the browser. |
| **Restricted Key** | Starts with `rk_test_` or `rk_live_`. **Recommended** — limit permissions to what WPsigner needs. |
| **Secret Key** | Starts with `sk_test_` or `sk_live_`. Supported for compatibility; prefer restricted keys when possible. |

Keys are stored encrypted on your server when OpenSSL is available.

### Default Currency

Choose the default currency for payment fields:

**USD**, **EUR**, **GBP**, **CAD**, **AUD**, **MXN**, **BRL**, **COP**

Individual Smart Form payment fields can override the currency per form.

---

## Webhook Configuration

Webhooks let WPsigner receive payment status updates from Stripe (succeeded, failed, disputes, refunds).

### Webhook URL

Copy the URL shown on the Stripe settings page:

```
https://your-site.com/wp-json/insigner/v1/stripe-webhook
```

Replace `your-site.com` with your WordPress site URL.

### Required Events

Enable these events in Stripe (or use **Create Webhook Automatically** in WPsigner):

| Event | Purpose |
|-------|---------|
| `payment_intent.succeeded` | Mark payment as completed |
| `payment_intent.payment_failed` | Handle failed attempts |
| `payment_intent.canceled` | Handle canceled intents |
| `charge.dispute.created` | Dispute notifications |
| `charge.refunded` | Refund tracking |

### Setup Options

**Option A — Automatic (recommended)**

1. Save your API keys first
2. Click **Create Webhook Automatically**
3. WPsigner registers the endpoint in Stripe and stores the signing secret

**Option B — Manual**

1. In [Stripe Webhooks](https://dashboard.stripe.com/webhooks), add the endpoint URL above
2. Select the events listed above
3. Copy the **Signing secret** (`whsec_...`)
4. Paste it into WPsigner and save

> **caution**
Use **test** webhooks with test keys and **live** webhooks with live keys. Mixing modes causes missed events.

---

## Test Connection

After saving keys:

1. Click **Test Connection**
2. WPsigner verifies the secret/restricted key against the Stripe API
3. Fix any errors before switching to Live Mode

---

## Payment on Document Signing

When a document includes a **Payment** field:

1. The signer completes payment in the signing UI (Stripe Payment Element)
2. WPsigner verifies the PaymentIntent on the server before the signature is accepted
3. Webhooks keep payment status in sync

Configure payment fields when creating or editing a document in the field editor.

---

## Payment in Smart Signing Forms

Smart Signing Forms uses the **same Stripe settings** as the core plugin.

1. Complete this Stripe setup first
2. In the Smart Forms builder, add a **Payment** field (one per form)
3. Set amount (minimum **0.50**, maximum **10,000** in the configured currency)
4. Signers enter a valid email, pay with **Pay now**, then sign and submit

See the full guide: [Smart Signing Forms — Payment field](/addons/smart-signing-forms/#payment-field-stripe)

---

## Troubleshooting

| Issue | Solution |
|-------|----------|
| **Not Connected** status | Enter both publishable and secret/restricted keys for the active mode |
| Test Connection fails | Verify key prefix matches mode (`pk_test_` + `rk_test_` in Test Mode) |
| Payments work but status not updating | Check webhook URL, signing secret, and required events |
| Smart Form shows "Stripe not configured" | Enable Stripe module and save valid keys |
| "Complete payment before submitting" | Signer must click **Pay now** and wait for success before **Submit & Sign** |
| Test badge still visible in production | Switch to **Live Mode** and use live keys |

### Debug Logging

Enable WordPress debug logging to inspect Stripe-related errors:

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

Check `wp-content/debug.log` for entries related to Stripe or Smart Forms payments.

---

## Next Steps

- [Smart Signing Forms](/addons/smart-signing-forms/) — Build forms with inline signature and payment
- [Form Fields](/core-features/form-fields/) — Payment fields on PDF documents
- [Account Portal](/addons/account-portal/) — Download plugins and addons with your license

---

# Microsoft Teams

> Send signing notifications to a Microsoft Teams channel via Incoming Webhooks

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/teams/
Markdown: https://docs.wpsigner.com/md/integrations/teams.md

Connect WPsigner with **Microsoft Teams** to receive rich signing notifications as Adaptive Cards in your team channels.

## Use Cases

| Scenario | Description |
|----------|-------------|
| **Team visibility** | Keep your entire team informed about document signing progress in a shared channel |
| **Manager oversight** | Let managers track who has signed and who is still pending without leaving Teams |
| **Sales workflows** | Notify the sales channel the moment a contract is fully executed |
| **HR onboarding** | Alert HR when a new hire completes their employment agreement |
| **Compliance tracking** | Maintain a real-time audit trail of signing events in a dedicated compliance channel |
| **Quick action** | Use Adaptive Card buttons to jump directly to the document management page |

## Prerequisites

- A Microsoft Teams workspace
- Permission to add connectors to a channel
- WPsigner v2.1.0 or later

## Compatibility

| Component | Minimum Version | Recommended |
|-----------|----------------|-------------|
| **WPsigner** | 2.1.0 | Latest |
| **WordPress** | 5.8 | 6.4+ |
| **PHP** | 7.4 | 8.1+ |
| **Microsoft Teams** | Any current plan | Microsoft 365 Business |
| **Adaptive Cards** | 1.2 | 1.2 |
| **TLS** | 1.2 | 1.3 |
| **PHP Extensions** | `curl`, `json` | `curl`, `json`, `openssl` |

## Setup Guide

### 1. Open Microsoft Teams

Navigate to the channel where you want to receive document signing notifications.

### 2. Add Incoming Webhook

1. Click the **...** menu next to the channel name
2. Select **Connectors** (or **Manage channel → Connectors**)
3. Find **Incoming Webhook** and click **Configure**

### 3. Name Your Webhook

1. Enter **"WPsigner"** as the name
2. Optionally upload a custom icon
3. Click **Create**
4. **Copy the webhook URL** — you'll need it in the next step

> **caution**
The webhook URL is a secret — anyone with it can post to your channel. WPsigner encrypts it at rest using AES-256-GCM and never displays it after saving.

### 4. Configure WPsigner

1. Go to **WPsigner → Integrations**
2. Click **Microsoft Teams**
3. Paste the **Webhook URL**
4. Enter a **Channel Name** label (for your reference)
5. Click **Test Connection** — check your Teams channel for a confirmation card
6. Toggle **Enable** on
7. Click **Save Settings**

## Notification Events

| Event | Trigger | Card Content |
|-------|---------|-------------|
| **Signing Request** | Signer added to document | Document name, signer details, View button |
| **Document Signed** | Signer completes signature | Who signed, progress count (e.g., 2/3) |
| **All Complete** | All signers finished | All signer names, Download button |

## Adaptive Cards

WPsigner sends notifications as [Adaptive Cards v1.2](https://adaptivecards.io/), which render natively in Microsoft Teams with:

- **Bold titles** with status indicators
- **FactSet** blocks with structured key-value data
- **Action buttons** (OpenUrl) linking to document management pages

## Developer Hooks

WPsigner fires the following WordPress action hooks during the signing lifecycle. You can use these to extend the Teams integration or build custom notification logic.

| Hook | Trigger | Parameters |
|------|---------|------------|
| `wps_signer_created` | A signer is assigned to a document | `$signer_id`, `$document_id` |
| `wps_after_document_signed` | A signer completes their signature | `$signer_id`, `$document_id` |
| `wps_document_completed` | All signers have signed the document | `$document_id` |

> **tip**
Use these hooks to send additional notifications, update external systems, or trigger custom automation. For example, you could post to a second Teams channel for a specific department.

```php
add_action( 'wps_document_completed', function( $document_id ) {
    // Custom logic when all signers have completed
    $document = wps_get_document( $document_id );
    // e.g., notify a different Teams channel or update a CRM
});
```

## Security

| Measure | Details |
|---------|---------|
| **Webhook URL** | Encrypted at rest (AES-256-GCM) |
| **Domain validation** | Must be on `*.webhook.office.com` |
| **AJAX Security** | Nonce + capability check + rate limiting |
| **Rate Limiting** | 5 tests/min, 10 saves/min per user |
| **Payload Size** | Enforced 28KB maximum |
| **API Rate Limit** | 4 requests/second (Teams enforced) |

## Troubleshooting

### "Invalid webhook URL"
- URL must start with `https://` and be on the `webhook.office.com` domain
- Re-copy from Teams — URLs expire if the connector is deleted

### No card appears in channel
- Check you're testing in the correct channel
- Verify the webhook connector is still active

### Card looks plain (no formatting)
- Adaptive Cards require Teams desktop or web client
- Mobile clients may render cards with reduced formatting

## Comparison

| Feature | Teams | Slack | Telegram | Twilio SMS |
|---------|-------|-------|----------|------------|
| Format | Adaptive Cards | Block Kit | HTML | Plain text |
| Auth | Webhook URL | Webhook URL | Bot Token | API Key |
| Cost | Free | Free | Free | Per message |
| Action buttons | Yes (OpenUrl) | Yes (Block actions) | Yes (Inline keyboard) | No |
| Progress tracking | Yes (FactSet) | Yes (Sections) | Yes (HTML) | No |

## Next Steps

- [Slack Integration](/integrations/slack/) — Post signing notifications to a Slack channel
- [Telegram Integration](/integrations/telegram/) — Notify signers via a Telegram bot
- [Twilio SMS Integration](/integrations/twilio/) — Send signing requests and reminders by SMS
- [WhatsApp Integration](/integrations/whatsapp/) — Deliver notifications via WhatsApp Business API
- [Zapier Integration](/integrations/zapier/) — Connect WPsigner with 5,000+ apps
- [Make Integration](/integrations/make/) — Build visual automation scenarios with Make
- [Pabbly Integration](/integrations/pabbly/) — Automate workflows through Pabbly Connect
- [All Integrations](/integrations/) — Browse every available WPsigner integration

---

# Telegram Bot Integration

> Send signing requests, reminders, and completion notifications via Telegram Bot API.

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/telegram/
Markdown: https://docs.wpsigner.com/md/integrations/telegram.md

Integrate WPsigner with [Telegram](https://telegram.org) to send signing requests and notifications directly to your signers via a Telegram bot — completely free, no monthly limits.

> **tip**
Telegram bots are free to create and use, with no message limits. Messages include inline buttons for one-tap signing access.

---

## Features

| Feature | Description |
|---------|-------------|
| **Signing Requests** | Send signature links with inline "Sign" button |
| **Reminders** | Manual reminders for pending signatures |
| **Completion Notifications** | Notify signers when document is complete |
| **Inline Buttons** | One-tap buttons to open signing pages |
| **Encrypted Token** | AES-256-GCM encryption for bot token |
| **HTML Formatting** | Rich messages with bold, italic, and links |

---

## Prerequisites

- ✅ WPsigner **2.1.0+**
- ✅ A **Telegram account** (free)
- ✅ A **Telegram bot** created via [@BotFather](https://t.me/BotFather)

---

## Step 1: Create a Telegram Bot

1. Open Telegram and search for **@BotFather** (or click [this link](https://t.me/BotFather))

2. Send the command `/newbot`

3. **Choose a name** for your bot (e.g., "MyCompany Signing Bot")

4. **Choose a username** — must end in `bot` (e.g., `mycompany_signing_bot`)

5. BotFather will reply with your **Bot Token**:

```
123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
```

> **caution**
Keep your bot token secret! Anyone with your token has full control over your bot.

---

## Step 2: Configure WPsigner

1. In WordPress admin, go to **WPsigner** → **Integrations**

2. Find **Telegram** and click **"Configure"**

3. Enable **"Telegram Notifications"** toggle

4. Paste your **Bot Token** from Step 1

5. Click **"Test Connection"** — you should see your bot's username

6. Click **"Save Settings"**

> **note**
The "Test Connection" button calls Telegram's `getMe` API to verify your token and display the bot username.

---

## Step 3: Get Signer Chat IDs

Telegram bots can only message users who have started a conversation first. Each signer needs a **Chat ID**.

### How Signers Get Their Chat ID

1. **The signer opens Telegram** and searches for your bot (e.g., `@mycompany_signing_bot`)

2. **The signer sends `/start`** to initiate the conversation

3. **The signer messages [@userinfobot](https://t.me/userinfobot)** — it will reply with their Chat ID:

```
Id: 123456789
```

4. **Share the Chat ID** with the document admin

> **tip**
You can also share a direct link to your bot: `https://t.me/your_bot_username`. This makes it easy for signers to find and start your bot.

---

## Step 4: Add Chat ID to Signers

When creating a document:

1. Go to **WPsigner** → **New Document**

2. In **Step 2 (Add Signers)**, enter the signer's **Telegram Chat ID** in the Telegram field

3. The signer will receive a Telegram message with a "Sign Document" button when the document is created

> **note**
Telegram is optional for each signer. Signers without a Chat ID will only receive email notifications.

---

## How It Works

```
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Create Document │────▶│  Signer Added   │────▶│  Telegram Sent  │
│  with Chat ID    │     │  (wps_signer_   │     │  with Sign      │
│                  │     │   created hook) │     │  Button          │
└─────────────────┘     └─────────────────┘     └─────────────────┘
                                                         │
                                                         ▼
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  Completion     │◀────│  Document       │◀────│  Signer Signs   │
│  Notification   │     │  Completed      │     │  Document       │
└─────────────────┘     └─────────────────┘     └─────────────────┘
```

### Automatic Notifications

| Event | Telegram Message |
|-------|-----------------|
| Signer added with Chat ID | 📝 Signing request + "Sign Document" button |
| Signer completes | ✅ Signature confirmation |
| All signers done | 🎉 Document completed notification |
| Manual reminder | ⏰ Reminder + "Sign Now" button |

---

## Message Examples

### Signing Request

```
📝 Document Ready for Signature

Hello John,

The document "Service Agreement" is ready for your signature.

This link expires in 48 hours.

[✍️ Sign Document]  ← Inline button
```

### Completion Notification

```
🎉 Document Completed

All signatures on "Service Agreement" are complete.

You will receive a copy via email shortly.
```

---

## Security

| Feature | Implementation |
|---------|----------------|
| **Token Encryption** | AES-256-GCM encryption at rest |
| **Nonce Verification** | CSRF protection on all AJAX requests |
| **Input Validation** | Chat ID format validation (numeric) |
| **Rate Limiting** | Telegram allows ~30 messages/second |

---

## API Reference

### Send Message Programmatically

```php
// Send a custom Telegram message
WPS_Telegram::send_message(
    '123456789',                // Chat ID
    '📝 <b>Custom message</b>', // HTML formatted text
    [                           // Optional inline keyboard
        'inline_keyboard' => [[
            ['text' => '🔗 Open Link', 'url' => 'https://...'],
        ]],
    ]
);
```

### Check Telegram Status

```php
if ( WPS_Telegram::is_enabled() && WPS_Telegram::is_configured() ) {
    // Telegram is ready
}
```

### Send Manual Reminder

```php
WPS_Telegram::send_reminder( $signer_id );
```

---

## Troubleshooting

### Bot Token Invalid

**Error:** "Unauthorized" or "Not Found"

**Solutions:**
1. Verify the token was copied correctly from BotFather
2. Token format should be `123456:ABC-DEF...`
3. Make sure there are no extra spaces
4. Try regenerating the token via `/token` command in BotFather

### Message Not Delivered

**Error:** "Bad Request: chat not found"

**Solutions:**
1. Verify the Chat ID is correct (numeric only)
2. The signer **must send `/start` to your bot first**
3. Bots cannot initiate conversations — this is a Telegram restriction
4. Ask the signer to message your bot and try again

### "Forbidden: bot was blocked by the user"

**Solutions:**
1. The signer has blocked your bot
2. Ask the signer to unblock the bot in their Telegram settings
3. The signer can search for your bot and send `/start` again

---

## Comparison: Telegram vs WhatsApp

| Feature | Telegram | WhatsApp |
|---------|----------|----------|
| **Cost** | Free | Paid after 1K msgs/month |
| **Setup** | @BotFather (2 min) | Meta Business Account + App |
| **Templates** | Free-form text | Pre-approved templates |
| **Rich Messages** | HTML + inline buttons | Template params only |
| **Rate Limit** | ~30 msg/sec | 250-100K/day (tiers) |
| **User ID** | Chat ID | Phone number (E.164) |

---

## FAQ

**Q: Is Telegram Bot API free?**
A: Yes, completely free with no message limits.

**Q: Can the bot message users first?**
A: No. Users must send `/start` to your bot before they can receive messages. This is a Telegram security restriction.

**Q: What happens if Telegram fails?**
A: Email notifications are always sent as a fallback. Telegram is an additional channel.

**Q: Can I use the same bot for multiple WPsigner installations?**
A: Yes, but each signer's Chat ID is universal. The bot will send messages from whichever installation triggers them.

---

## Next Steps

- [WhatsApp Integration](/integrations/whatsapp/) — Send via WhatsApp Business API
- [API Keys](/api/) — Set up REST API access
- [Webhooks](/api/webhooks/) — Handle document events programmatically

---

# Twilio SMS

> Send signing requests and reminders via SMS using Twilio

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/twilio/
Markdown: https://docs.wpsigner.com/md/integrations/twilio.md

Connect WPsigner with **Twilio** to send signing requests, confirmations, and reminders via SMS to your signers' mobile phones.

## Use Cases

| Scenario | Description |
|----------|-------------|
| **Remote signers** | Reach signers who may not check email frequently by delivering signing links via SMS |
| **Urgent documents** | Send time-sensitive signing requests that demand immediate attention |
| **Two-factor delivery** | Combine email and SMS channels to ensure signing requests are received |
| **Automated reminders** | Nudge pending signers with SMS reminders to reduce turnaround time |
| **Confirmation receipts** | Notify signers via SMS the moment their signature is recorded |
| **Completion alerts** | Alert all parties by text when every signer has completed the document |

## Prerequisites

- A [Twilio account](https://www.twilio.com/try-twilio) (free trial available)
- A Twilio phone number with SMS capabilities
- WPsigner v2.1.0 or later

## Compatibility

| Component | Minimum Version | Recommended |
|-----------|----------------|-------------|
| **WPsigner** | 2.1.0 | Latest |
| **WordPress** | 5.8 | 6.4+ |
| **PHP** | 7.4 | 8.1+ |
| **Twilio API** | 2010-04-01 | 2010-04-01 |
| **TLS** | 1.2 | 1.3 |
| **PHP Extensions** | `curl`, `json` | `curl`, `json`, `openssl` |

## Setup Guide

### 1. Create a Twilio Account

1. Go to [twilio.com](https://www.twilio.com/try-twilio) and sign up
2. Verify your email and phone number
3. Free trial accounts include test credits

### 2. Get a Phone Number

1. In the **Twilio Console**, navigate to **Phone Numbers → Manage → Buy a Number**
2. Select a number with **SMS** capabilities
3. Note down the phone number (in E.164 format, e.g., `+15551234567`)

### 3. Copy Your Credentials

1. From the Twilio Console **Dashboard**, locate:
   - **Account SID** — starts with `AC` followed by 32 characters
   - **Auth Token** — click the eye icon to reveal

> **caution**
Your Auth Token is a secret credential. Never share it or commit it to version control. WPsigner encrypts it at rest using AES-256-GCM.

### 4. Configure WPsigner

1. Go to **WPsigner → Integrations**
2. Click **Twilio SMS**
3. Enter your **Account SID**, **Auth Token**, and **From Number**
4. Click **Test Connection** to verify
5. Toggle **Enable** on
6. Click **Save Settings**

## Notification Events

| Event | Trigger | Recipient |
|-------|---------|-----------|
| **Signing Request** | Signer added to document | Signer (if phone provided) |
| **Signature Recorded** | Signer completes signature | Signer |
| **Document Complete** | All signers finished | All signers with phone numbers |
| **Reminder** | Admin-initiated | Pending signer |

> **note**
SMS notifications are only sent to signers who have a phone number in their profile. Email notifications continue to work for all signers.

## Phone Number Format

All phone numbers must be in **E.164 format**:

| Format | Valid? |
|--------|--------|
| `+15551234567` | Yes |
| `+442071234567` | Yes |
| `5551234567` | No — Missing country code |
| `(555) 123-4567` | No — Formatting characters |

## Security

| Measure | Details |
|---------|---------|
| **Auth Token** | Encrypted at rest (AES-256-GCM) |
| **Account SID** | Stored in `wp_options` |
| **API Communication** | HTTPS only (TLS 1.2+) |
| **AJAX Security** | Nonce + capability check + rate limiting |
| **Rate Limiting** | 5 tests/min, 10 saves/min per user |

## API Reference

WPsigner uses the [Twilio REST API](https://www.twilio.com/docs/messaging/api/message-resource):

```
POST https://api.twilio.com/2010-04-01/Accounts/{SID}/Messages.json
```

- **Authentication**: HTTP Basic (Account SID : Auth Token)
- **Content-Type**: `application/x-www-form-urlencoded`
- **Parameters**: `To`, `From`, `Body`

## Troubleshooting

### "Connection failed" on test
- Verify your Account SID starts with `AC` and is exactly 34 characters
- Re-enter your Auth Token (it's not displayed after saving)
- Check your account status at [twilio.com/console](https://www.twilio.com/console)

### SMS not received
- Verify the recipient number is in E.164 format
- Check your Twilio balance and trial limitations
- Trial accounts can only send to verified numbers

### Error code 21608
- Your Twilio number doesn't have SMS capability
- Purchase a number with SMS enabled

## Comparison

| Feature | Twilio SMS | WhatsApp | Telegram | Slack |
|---------|-----------|----------|----------|-------|
| Type | SMS | Business API | Bot API | Webhook |
| Reach | Any phone | WhatsApp users | Telegram users | Team channel |
| Cost | Per message | Per message | Free | Free |
| Rich formatting | No | Yes | Yes (HTML) | Yes (Block Kit) |
| Action buttons | No | No | Inline keyboard | Block actions |
| Setup complexity | Low | Medium | Low | Low |

## Next Steps

- [WhatsApp Integration](/integrations/whatsapp/) — Send signing notifications via WhatsApp Business API
- [Telegram Integration](/integrations/telegram/) — Notify signers through a Telegram bot
- [Slack Integration](/integrations/slack/) — Post signing updates to a Slack channel
- [Microsoft Teams Integration](/integrations/teams/) — Receive Adaptive Card notifications in Teams
- [Zapier Integration](/integrations/zapier/) — Connect WPsigner with 5,000+ apps via Zapier
- [n8n Integration](/integrations/n8n/) — Build custom automation workflows with n8n
- [All Integrations](/integrations/) — Browse every available WPsigner integration

---

# Wasabi

> Hot cloud storage for signed documents — no egress fees

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/wasabi/
Markdown: https://docs.wpsigner.com/md/integrations/wasabi.md

Back up signed PDFs to **Wasabi** — an S3-compatible service with no egress fees and predictable pricing.

> **tip**
Wasabi uses the same S3 API as Amazon. The only difference is the endpoint and available regions.

---

## Requirements

- WPsigner **2.1.0+**
- A [Wasabi](https://wasabi.com/) account
- Access key and secret key from the Wasabi console

---

## How It Works

When a signer completes a document, WPsigner automatically uploads the final PDF to your Wasabi bucket. The flow is identical to the Amazon S3 integration because Wasabi implements the same S3 API:

1. **Document signed** — All required signers complete the document.
2. **PDF generated** — WPsigner creates the final certified PDF.
3. **Upload triggered** — The plugin sends the PDF to your Wasabi bucket using the S3 `PutObject` operation with AWS Signature V4 authentication.
4. **Confirmation stored** — WPsigner records the remote URL and upload status in the document metadata.

> **note**
Uploads happen server-side in the background. Signers are not affected by the upload process and receive their confirmation immediately.

---

## Setup

### Step 1: Create a Bucket

1. Go to the [Wasabi Console](https://console.wasabisys.com/).
2. Click **Create Bucket**.
3. Choose a unique bucket name (lowercase, no spaces). Example: `wpsigner-signed-docs`.
4. Select the region closest to your WordPress server for the best upload performance.
5. Leave versioning and logging at their defaults unless your organization requires them.
6. Click **Create Bucket**.

> **caution**
Bucket names are globally unique across all Wasabi accounts. If the name is already taken, you will need to choose a different one.

### Step 2: Create Access Keys

1. Go to **Access Keys** in the Wasabi console sidebar.
2. Click **Create New Access Key**.
3. Copy both the **Access Key** and **Secret Key** immediately — the secret key is shown only once.

> **important**
Store these credentials in a secure location. If you lose the secret key, you must generate a new key pair.

### Step 3: Configure WPsigner

1. Go to **WPsigner → Integrations → Wasabi** in your WordPress admin.
2. Enter the **Access Key** and **Secret Key** from Step 2.
3. Enter the **Bucket Name** exactly as created in Step 1.
4. Select the **Region** that matches the region you chose when creating the bucket.
5. Click **Test Connection** to verify the credentials and bucket access.
6. Once the test passes, click **Save Settings**.

> **tip**
If the test connection fails, double-check that the region in WPsigner matches the region you selected in the Wasabi console. A mismatch is the most common configuration error.

---

## Use Cases

| Use Case | Description |
|----------|-------------|
| **Long-term archival** | Store signed contracts and agreements for years at a flat, predictable cost with no surprise egress fees. |
| **Regulatory compliance** | Keep tamper-proof copies of signed documents outside your WordPress server to meet audit and retention requirements. |
| **Disaster recovery** | Maintain an off-site backup of all signed PDFs in case of server failure or data loss. |
| **High-volume operations** | Organizations that process hundreds of documents per month benefit from Wasabi's low per-TB pricing. |
| **Multi-site backup** | Centralize signed document storage from multiple WordPress installations into a single Wasabi bucket. |

---

## Wasabi vs Amazon S3

| Feature | Wasabi | Amazon S3 |
|---------|--------|-----------|
| **Egress Fees** | None | $0.09/GB |
| **Storage Cost** | $6.99/TB/mo | $23/TB/mo |
| **API** | S3-compatible | Native S3 |
| **Regions** | 13 | 21+ |
| **Min Storage** | 1 TB | None |

---

## Security

Same security as Amazon S3: AWS Signature V4, AES-256-GCM encryption, rate limiting, nonce verification.

---

## Troubleshooting

| Problem | Cause | Solution |
|---------|-------|----------|
| **Test connection fails with "InvalidAccessKeyId"** | The access key is incorrect or has been deactivated. | Verify the access key in the Wasabi console under **Access Keys**. Generate a new pair if needed. |
| **Test connection fails with "SignatureDoesNotMatch"** | The secret key is incorrect or contains trailing whitespace. | Re-paste the secret key, making sure there are no extra spaces before or after it. |
| **"NoSuchBucket" error** | The bucket name in WPsigner does not match an existing bucket. | Check the exact bucket name in the Wasabi console and re-enter it in WPsigner. |
| **"Region mismatch" or "PermanentRedirect"** | The region selected in WPsigner does not match the bucket's actual region. | Open the bucket properties in Wasabi to confirm the region, then update the WPsigner setting to match. |
| **Uploads succeed but files are empty (0 bytes)** | Server memory or execution time limits are too low to process large PDFs. | Increase `memory_limit` to at least `256M` and `max_execution_time` to `120` in your `php.ini`. |
| **Intermittent timeout errors** | Network instability or high latency between your server and the Wasabi region. | Choose a Wasabi region geographically closer to your hosting server, or increase the WPsigner timeout value. |

---

## Compatibility

| Component | Minimum Version | Recommended |
|-----------|----------------|-------------|
| **WPsigner** | 2.1.0 | Latest |
| **WordPress** | 5.8 | 6.4+ |
| **PHP** | 7.4 | 8.1+ |
| **cURL extension** | Required | Latest |
| **OpenSSL extension** | Required | Latest |

---

## Next Steps

- [Amazon S3](/integrations/amazon-s3/) — Native AWS storage with the broadest region coverage.
- [Cloudflare R2](/integrations/cloudflare-r2/) — Zero egress fees with a built-in global CDN.
- [Google Drive](/integrations/google-drive/) — Sync signed documents to your Google Workspace.
- [Dropbox](/integrations/dropbox/) — Automatic backups to your Dropbox account.

---

# WhatsApp Business Integration

> Send signing requests, reminders, and completion notifications via WhatsApp using Meta Cloud API (Graph API v25.0). Manual Complete Setup flow for WPsigner 3.0.6+.

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/whatsapp/
Markdown: https://docs.wpsigner.com/md/integrations/whatsapp.md

<div style="display:flex;flex-wrap:wrap;gap:0.75rem;align-items:center;margin:0 0 1.25rem;">
  
  
  
    Markdown for LLMs
  
</div>

Send document signing requests and notifications via the official **WhatsApp Cloud API** (Meta). The current setup path is **step-by-step**: copy credentials from Meta, then use **Complete Setup** in WPsigner.

Prefer the permanent **System User** token. Temporary tokens from API Setup expire in about 24 hours and will break production.

**One-click Connect with Meta** (Embedded Signup) is implemented in the product but **hidden for now** until Meta approves WPsigner as a Tech Provider. Use Complete Setup below. LLM-oriented copy of this guide: [whatsapp.md](/integrations/whatsapp.md).

---

## Features

| Feature | Description |
|---------|-------------|
| **Signing requests** | Same timing as the signing-request email |
| **Signature recorded** | Confirm when an individual signer finishes |
| **Document complete** | Notify when all required signatures are collected |
| **Declined** | Notify when a signer declines |
| **Reminders** | Manual and scheduled reminders |
| **OTP (AUTH)** | Optional WhatsApp OTP template for advanced signing |
| **In-plugin templates** | Create / sync default Meta templates from WPsigner |
| **Complete Setup** | One admin action: save, test, detect WABA, create templates, enable notifies |
| **Encrypted credentials** | AES-256-GCM at rest |

---

## Prerequisites

- A **Meta Business Account**
- A **Meta app** with the **WhatsApp** product
- A **Phone Number ID** (test number or production business number)
- A **permanent System User access token** with:
  - `whatsapp_business_management`
  - `whatsapp_business_messaging`
- WPsigner **3.0.6+** (Complete Setup + current template definitions)

---

## Step 1: Create the Meta app

1. Open [developers.facebook.com/apps](https://developers.facebook.com/apps/) and click **Create App**.

2. Choose the use case **Connect with customers through WhatsApp** (recommended), or create a Business app and add the WhatsApp product later.

3. Link or create a **Meta Business** portfolio when prompted.

4. Finish the wizard until you reach **WhatsApp → API Setup**. Meta provides a **test phone number** for development.

---

## Step 2: Get your credentials

### Phone Number ID

1. In the app, open **WhatsApp → API Setup**.

2. Under **From**, copy the **Phone number ID** (digits only, e.g. `123456789012345`).

3. For production, add and verify your own business phone number (not a personal WhatsApp number).

### Access token

**Recommended — System User token**

1. Note your **App ID** under **App settings → Basic**.

2. Open [System users](https://business.facebook.com/settings/system-users) in Meta Business Suite.

3. **Add** a system user (e.g. `WPsigner Integration`), role **Admin**.

4. **Add assets** → assign your **WhatsApp Business Account** with full control.

5. **Generate new token**:
   - Select your Meta app
   - Choose a long-lived / never-expire option when available
   - Permissions: `whatsapp_business_management`, `whatsapp_business_messaging`

6. Copy the token and store it securely (shown once).

1. WhatsApp → **API Setup** → copy the temporary token.  
2. Valid for ~24 hours. **Do not use in production.**

### Business Account ID (WABA)

Needed for template management. You can:

- Paste it manually (WhatsApp Business Account ID from Business Suite / WhatsApp Manager), or  
- Leave it blank and let **Complete Setup** try to **auto-detect** it from Phone Number ID + token.

---

## Step 3: Complete Setup in WPsigner

1. In WordPress, go to **WPsigner → Integrations → WhatsApp Business**.

2. Paste **Phone Number ID** and **Permanent Access Token**.

3. Optionally set **template language** and WABA ID.

4. Click **Complete Setup**. WPsigner will:
   - Save credentials (encrypted)
   - Test the connection
   - Detect WABA when possible
   - Create any **missing default templates** in Meta
   - Turn on notification toggles

5. Open **Message Templates** and click **Refresh** until required templates show **APPROVED** (or use **Create Missing Templates** if some are still missing).

6. Confirm the checklist: credentials, WABA, templates, notifications enabled.

You can still use **Test Connection** and **Save Settings** without Complete Setup if you want finer control. Template creation still needs a valid WABA ID.

---

## Step 4: Default templates

WPsigner creates these templates via the Meta API (**named parameters**, not `{{1}}` / `{{2}}` body vars). Prefer creating them from the plugin rather than hand-building outdated positional templates.

| Template | Category | Purpose |
|----------|----------|---------|
| `signing_request` | UTILITY | Signing link (+ URL button) |
| `signing_signed` | UTILITY | Individual signature recorded |
| `signing_completed` | UTILITY | All parties finished |
| `signing_reminder` | UTILITY | Reminder (+ URL button) |
| `signing_declined` | UTILITY | Decline notice |
| `otp_verification` | AUTHENTICATION | OTP copy-code (advanced signing) |

Named body parameters (examples):

- `signing_request`: `signer_name`, `document_title`, `expire_hours`
- `signing_signed`: `signer_name`, `document_title`
- `signing_completed`: `document_title`, `signed_date`, `brand_name`
- `signing_reminder`: `document_title`, `deadline`
- `signing_declined`: `signer_name`, `document_title`, `decline_reason`

Meta must **approve** templates before send. Utility templates are often approved within minutes to hours; Authentication can take longer.

---

## Step 5: Add signer phone numbers

1. Create or edit a document / campaign signer.

2. Enter the WhatsApp number in **E.164** form (country code included), e.g. `+15551234567`.

3. Signers without a valid phone only receive **email**.

---

## Notification events

Toggle each event under WhatsApp settings:

| Event | Template | Trigger |
|-------|----------|---------|
| Signing Request | `signing_request` | `wps_signing_request_sent` (same timing as email) |
| Signature Recorded | `signing_signed` | `wps_after_document_signed` |
| Document Complete | `signing_completed` | `wps_document_completed` |
| Document Declined | `signing_declined` | `wps_document_declined` |
| Reminders | `signing_reminder` | `wps_reminder_sent` |

---

## How it works

```text
Create document + signer phone
        │
        ▼
Signing request email timing ──► WhatsApp signing_request (if enabled)
        │
        ▼
Signer signs ──► signing_signed (optional)
        │
        ▼
All done ──► signing_completed
```

---

## Security

| Feature | Detail |
|---------|--------|
| Token / ID storage | Encrypted at rest (AES-256-GCM) |
| AJAX | Nonce + `manage_options` (or settings capability) |
| Phone validation | E.164 before send |
| Rate limits | Test / save / sync throttles in admin |
| Audit | Settings and template actions logged when Audit is available |

---

## Troubleshooting

1. Phone Number ID must be **numeric only**.  
2. Use a **permanent** token (temporary tokens expire).  
3. Confirm WhatsApp is added to the Meta app.  
4. System user must have the WhatsApp asset + messaging permissions.

1. Ensure WABA ID is saved (Complete Setup or manual).  
2. Use **Create Missing Templates** then **Refresh**.  
3. Wait for Meta **APPROVED** before expecting delivery.  
4. Language in WPsigner must match the template language you created.

1. Phone must be E.164 and have WhatsApp.  
2. Template must be **APPROVED**.  
3. Check Meta quality rating and messaging limits.  
4. Sandbox / test numbers can only message allowed recipients.

If you see Meta saying the business **cannot register customers**, that is a Meta Tech Provider / verification / App Review limit — not a WPsigner bug. Keep using **Complete Setup** until one-click is re-enabled.

### Meta messaging tiers (summary)

| Tier | Typical daily limit | Notes |
|------|---------------------|--------|
| Unverified | ~250 | Raise via business verification |
| Verified tiers | 1k → 100k+ | Depends on quality rating |

---

## Developer reference

PHP class: `\InSigner\Integrations\Messaging\WhatsApp`

| Method | Description |
|--------|-------------|
| `is_enabled()` | Notifications toggle |
| `is_configured()` | Phone ID + token present |
| `validate_phone( $phone )` | E.164 check |
| `test_connection( $phone_id, $token )` | Graph phone lookup |
| `get_default_templates()` | Template definitions |
| `sync_default_templates()` | Create missing defaults |
| `get_setup_status( $refresh )` | Admin checklist data |

```php
use InSigner\Integrations\Messaging\WhatsApp;

if ( WhatsApp::is_enabled() && WhatsApp::is_configured() ) {
    // Ready to send via hooks / templates
}

if ( WhatsApp::validate_phone( '+15551234567' ) ) {
    // Store on signer
}
```

Graph base URL constant: `WhatsApp::API_BASE` → `https://graph.facebook.com/v25.0`.

---

## FAQ

**Is WhatsApp API free?**  
Meta offers a free conversation allowance; overage is billed per Meta’s regional rates. Customers attach payment on their WABA when required.

**Can I use a personal WhatsApp number?**  
No. Use a number registered for WhatsApp Cloud API / Business Platform.

**Do I still need email?**  
Yes. Email remains the primary channel; WhatsApp is additive when a valid phone is present.

**When will one-click Connect return?**  
After Meta allows customer onboarding for the WPsigner business app (Tech Provider / App Review). Then it can be turned on with `WPS_WHATSAPP_EMBEDDED_SIGNUP`.

---

## Next steps

- [Didit KYC](/integrations/didit/) — identity verification before signing  
- [Email templates](/customization/email-templates/) — parallel email channel  
- [Webhooks](/api/webhooks/) — react to document events in your stack  
- [Markdown for LLMs](/integrations/whatsapp.md) — plain-text copy of this guide

---

# WooCommerce

> Automatically generate signing documents from WooCommerce orders.

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/woocommerce/
Markdown: https://docs.wpsigner.com/md/integrations/woocommerce.md

## Overview

The WooCommerce integration creates WPsigner documents automatically when orders reach a specific status. When a customer completes a purchase, you can generate a contract, service agreement, or any document — pre-filled with billing, shipping, and order data — and send it for signing.

> **tip**
This is a "post-purchase" integration. The document is created **after** the order changes to your configured status (e.g., Completed, Processing).

## Prerequisites

- **WPsigner** v2.0.0 or later
- **WooCommerce** v6.0 or later (HPOS compatible)
- At least one WPsigner **template** created

## Setup

1. Go to **WPsigner → Integrations**
2. Find the **WooCommerce** card and click **Configure**
3. Click **New Feed** to create your first automation

## Feed Configuration

Each feed connects an order status to a WPsigner template:

| Field | Description |
|-------|-------------|
| **Feed Name** | Label for your reference (e.g., "Service Agreement") |
| **Trigger Status** | Order status that creates the document (Processing, Completed, On Hold, etc.) |
| **Product Filter** | Restrict to specific products. Leave empty for all products. |
| **Template** | Which WPsigner template to use |
| **Document Title** | Supports variables: `{{signer_name}}`, `{{order_id}}`, `{{billing_name}}`, `{{date}}` |
| **Signer Name/Email** | Which order field provides the signer's identity |
| **Auto-send** | Immediately send the signing email |

## Available Order Fields

All fields are extracted using the `WC_Order` API for full HPOS compatibility.

### Billing
| Field Key | Description |
|-----------|-------------|
| `billing_name` | Full name (first + last) |
| `billing_first_name` | First name |
| `billing_last_name` | Last name |
| `billing_email` | Email address |
| `billing_phone` | Phone number |
| `billing_company` | Company name |
| `billing_address_1` | Address line 1 |
| `billing_address_2` | Address line 2 |
| `billing_city` | City |
| `billing_state` | State/Province |
| `billing_postcode` | ZIP/Postcode |
| `billing_country` | Country code |

### Shipping
| Field Key | Description |
|-----------|-------------|
| `shipping_name` | Full name |
| `shipping_first_name` | First name |
| `shipping_last_name` | Last name |
| `shipping_company` | Company |
| `shipping_address_1` | Address line 1 |
| `shipping_city` | City |
| `shipping_state` | State/Province |
| `shipping_postcode` | ZIP/Postcode |
| `shipping_country` | Country code |

### Order Details
| Field Key | Description |
|-----------|-------------|
| `order_id` | Order ID |
| `order_number` | Order number (may differ from ID) |
| `order_total` | Total amount |
| `order_subtotal` | Subtotal before tax/shipping |
| `order_tax` | Tax total |
| `order_shipping_total` | Shipping total |
| `order_discount` | Discount total |
| `order_currency` | Currency code (e.g., USD) |
| `order_date` | Date created |
| `customer_note` | Customer's order note |
| `payment_method` | Payment method slug |
| `payment_method_title` | Payment method label |
| `product_names` | Comma-separated product names |
| `product_skus` | Comma-separated SKUs |

### Custom Meta
Use the `meta_` prefix to access custom order meta fields:
```
meta_your_custom_field
```

## Variable Mapping

Map order fields to template variables using the **Variable Mapping** section in the feed editor:

| Variable (Template) | Order Field | Result |
|---------------------|-------------|--------|
| `custom.company` | `billing_company` | Company name from billing |
| `custom.phone` | `billing_phone` | Phone number |
| `custom.total` | `order_total` | Order total amount |
| `custom.products` | `product_names` | List of purchased products |

In your template, reference these with `{{custom.company}}`, `{{custom.phone}}`, etc.

## Product Filtering

By default, a feed triggers for **all products**. To restrict it:

1. In the feed editor, select specific products from the **Product Filter** dropdown
2. Hold `Ctrl` (Windows) or `Cmd` (Mac) to select multiple
3. The feed only triggers if the order contains at least one matching product

> **note**
Variation products are also checked — if a parent product matches, the feed triggers.

## Security Measures

| Measure | Implementation |
|---------|---------------|
| **Nonce verification** | All 5 AJAX endpoints verify `wps_ajax_nonce` |
| **Capability check** | Admin operations require `manage_options` |
| **Rate limiting** | Max 10 documents/minute per order (transient-based) |
| **Duplicate prevention** | Order meta tracks which feeds have been processed |
| **Status validation** | Trigger status validated against WC registered statuses |
| **Field whitelist** | Signer fields validated against allowed order fields |
| **Email validation** | `sanitize_email()` + `is_email()` double check |
| **Error logging** | Sensitive data redacted, only logs with `WP_DEBUG` |

## Developer Hooks

### `wps_woocommerce_document_created`

Fires after a document is created from a WooCommerce order.

```php
add_action('wps_woocommerce_document_created', function($document_id, $order, $feed, $feed_id) {
    // $document_id - int - The created document ID
    // $order       - WC_Order - The WooCommerce order object
    // $feed        - array - Feed configuration used
    // $feed_id     - string - Feed identifier
    
    // Example: add custom order note
    $order->add_order_note(
        sprintf('WPsigner document #%d created.', $document_id)
    );
}, 10, 4);
```

## Troubleshooting

### Document not created after order
1. Verify the feed is **enabled**
2. Check the **trigger status** matches the order's new status
3. If product filter is set, ensure the order contains a matching product
4. Check WP debug log for `[WPsigner WooCommerce]` entries

### Duplicate documents
Each feed stores a `_wps_wc_processed_{feed_id}` meta on the order. If an order is reprocessed (e.g., status changed back and forth), it won't create duplicates for the same feed.

### WooCommerce not detected
The integration requires `class_exists('WooCommerce')` to be true. Ensure WooCommerce is active and not deactivated by another plugin.

### HPOS Compatibility
This integration uses `WC_Order` getters exclusively (`$order->get_billing_email()`, etc.) and `$order->update_meta_data()` + `$order->save()` for meta storage. It is fully compatible with WooCommerce HPOS (High-Performance Order Storage).

---

# WPForms Integration

> Automate document signing from WPForms submissions with WPsigner's native integration. Works with WPForms Lite and Pro.

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/wpforms/
Markdown: https://docs.wpsigner.com/md/integrations/wpforms.md

Automate your document signing workflow by connecting **WPForms** with **WPsigner**. You can send a PDF template for signature after submit, or use **content signing** with the WPsigner Signing field (inline signature + optional OTP).

> **note**
This integration works with both **WPForms Lite** (free) and **WPForms Pro**. No additional plugins or add-ons are required. **WPsigner 3.1.1+** is recommended for OTP reliability and content-signing feed save.

---

## Choose your workflow

| Workflow | Best for | Template required? |
|----------|----------|--------------------|
| **PDF template + feed** (below) | Fixed contracts (NDA, agreement) filled from the form | Yes |
| **Content signing** | The form answers *are* the document; signer draws/types on the form | No |

---

## How template feeds work

Each **PDF template** feed connects one WPForms form to one WPsigner Template:

```
Form Submitted → Feed Triggers → Document Created from Template → Email Sent to Signer
```

| Component | Description |
|-----------|-------------|
| **Feed** | A connection rule that links a form to a template |
| **Template** | A pre-configured PDF with signature fields positioned via drag & drop |
| **Signer Mapping** | Links form fields (Name, Email) to the document signer |
| **Variable Mapping** | Passes extra form data into template fields (use the field **Mapping name**) |
| **Auto-send** | Optionally sends the signing email immediately |

### Content signing (inline)

1. Add one or more **WPsigner Signing** fields to your WPForms form (plus Name and Email).
2. Optionally enable **OTP** on a signing field (verification is shared for the form).
3. On submit, WPsigner builds the signed PDF from the submission (no PDF template required). Every filled signing field is included on the PDF with its label.
4. If you create a feed only for CC / title, you still only need **Feed Name** and **Form** — a template is not required for content signing.

> **Name vs Username**
Map a real **Name** field for the signer. Avoid using a field labeled only “Username” as the person name.

---

## Prerequisites

Before setting up the integration, you need:

1. **WPsigner** installed and activated (v1.4.0+)
2. **WPForms** installed and activated (Lite or Pro)
3. At least one **WPForms form** created with name and email fields
4. At least one **WPsigner Template** with signature fields configured

---

## Step 1: Create a WPsigner Template

A template is a reusable PDF document with pre-positioned fields (signature, name, email, date, etc.). The template defines where the signer will sign and what information they need to fill in.

### Creating a Template from the Document Editor

1. Go to **WPsigner → New Document**
2. **Upload your PDF** document (e.g., NDA, contract, service agreement)
3. **Add a signer** in Step 2 (Signers)
   - Enter any placeholder name and email — these will be replaced by the form data
4. **Position your fields** in Step 3 (Fields)
   - Drag and drop **Signature**, **Name**, **Email**, **Date**, or **Text** fields onto the PDF
   - **Double-click** each field to set **Field name**, optional **Mapping name**, and **Placeholder**
   - Resize fields as needed
5. Go to **Step 4 (Review)** and click **Save as Template**
6. Enter a **template name** (e.g., "NDA Contract") and optional description
7. Click **Save Template**

> **tip**
Clear **Field names** make feed mapping easier. Use short **Mapping names** (e.g. `company`, `phone`) if you will connect Zapier or typed variable keys. See [Form fields](/core-features/form-fields/#field-name-mapping-name-and-placeholder).

### Verifying Your Template

After saving, verify the template was created correctly:

1. Go to **WPsigner → Templates**
2. You should see your new template listed with the field count
3. You can preview the template to confirm field positions

---

## Step 2: Create a WPForms Form

If you don't already have a form, create one in WPForms:

1. Go to **WPForms → Add New**
2. Choose a template or start from a **Blank Form**
3. Add at minimum:
   - A **Name** field (Simple or First/Last)
   - An **Email** field
4. You can add additional fields that will be available for variable mapping (company, phone, address, etc.)
5. **Save** the form and embed it on a page

### Recommended Form Fields

| Field | Purpose | Required |
|-------|---------|----------|
| **Name** | Maps to document signer name | ✅ Yes |
| **Email** | Maps to signer email for signing link | ✅ Yes |
| **Single Line Text** (Company) | Can be mapped to template variables | Optional |
| **Phone** | Can be mapped to template variables | Optional |
| **Address** | Can be mapped to template variables | Optional |
| **Paragraph Text** | Any additional data for the document | Optional |

> **note**
WPForms Lite includes all the field types you need for this integration. Pro is not required.

---

## Step 3: Connect the Integration

### Navigate to the Integration Page

1. Go to **WPsigner → Integrations** in your WordPress admin
2. Locate the **WPForms** card under WordPress Plugins
3. Click **Configure**

You'll see the WPForms Integration page with:
- A status badge showing **Connected** (if WPForms is active)
- The count of available forms and configured feeds
- A **New Feed** button

> **caution**
If you see a warning that "WPForms is not installed", you need to install and activate WPForms first. Click the **Install WPForms** button to go to the plugin installer.

### Create a New Feed

1. Click the **New Feed** button
2. A modal window will appear with the following configuration options:

#### Feed Name
- Give your feed a descriptive name (e.g., "NDA Contract Feed", "Service Agreement - Signup")
- This name is shown in the feeds list for easy identification

#### WPForms Form
- Select the WPForms form that will trigger this integration from the dropdown
- All active WPForms forms on your site will be listed
- When you select a form, its fields are automatically loaded for mapping

#### WPsigner Template
- Select the WPsigner template to use when creating documents
- Only templates with configured fields will work correctly

#### Document Title
- Define the title for each generated document
- You can use **dynamic variables** that will be replaced with actual data:

| Variable | Replaced With | Example |
|----------|---------------|---------|
| `{{signer_name}}` | Signer's full name | John Smith |
| `{{signer_email}}` | Signer's email | john@example.com |
| `{{date}}` | Current date | 2026-02-07 |
| `{{company_name}}` | Company name (if mapped) | Acme Corp |

**Example title:** `NDA - {{signer_name}} - {{date}}`
This would generate: "NDA - John Smith - 2026-02-07"

#### Primary Signer

Map the form fields to the signer information:

- **Name Field**: Select which WPForms field contains the signer's name
- **Email Field**: Select which WPForms field contains the signer's email
  - The signing link will be sent to this email address

> **important**
Both the Name and Email fields are **required**. If either is missing when the form is submitted, the integration will skip the submission and log the error.

#### Variable Mapping (Optional)

Map additional form fields to template variables. This allows you to pass extra data from the form into the document:

1. Click **Add** to add a new mapping row
2. In the left input, enter the variable name (e.g., `custom.company`, `custom.phone`)
3. In the right dropdown, select the corresponding WPForms field
4. Repeat for each variable you want to map

**Example mappings:**

| Variable | Form Field | Use Case |
|----------|------------|----------|
| `custom.company` | Company Name | Include company in document |
| `custom.phone` | Phone Number | Add phone to document |
| `custom.address` | Address | Include address in document |
| `custom.amount` | Payment Amount | Add pricing to contract |

#### Auto-send Document

- **Enabled** ✅ (default): The signer receives an email with the signing link immediately after form submission
- **Disabled**: The document is created but stays in "Draft" status. You can manually send it later from the WPsigner dashboard

#### Feed Enabled

- **Enabled** ✅ (default): The feed is active and will trigger on form submissions
- **Disabled**: The feed is paused and will not create documents

### Save the Feed

Click **Save Feed** to save the configuration. The page will reload and you'll see your new feed in the list.

---

## Step 4: Test the Integration

1. **Open your WPForms form** on the frontend
2. **Fill in the form** with test data:
   - Enter your own name and email so you receive the signing email
3. **Submit the form**
4. **Check your email** for the signing link (if Auto-send is enabled)
5. **Verify in WPsigner**:
   - Go to **WPsigner → Documents**
   - You should see a new document with the title you configured
   - Open the document to verify:
     - The signer's name and email are correct
     - The fields (signature, name, email, date) are positioned correctly on the PDF
6. **Complete the signing** to verify the full end-to-end flow

---

## Managing Feeds

### Edit a Feed

1. Go to **WPsigner → Integrations → WPForms**
2. Click the **Edit** button on the feed card
3. Modify the settings as needed
4. Click **Save Feed**

### Enable/Disable a Feed

- Click the **Enable** or **Disable** button on the feed card to toggle the feed status
- Disabled feeds will not trigger on form submissions

### Delete a Feed

1. Click the **Delete** button (trash icon) on the feed card
2. Confirm the deletion
3. The feed will be permanently removed

> **note**
Deleting a feed does not affect documents that were already created. Only future form submissions will stop creating documents.

---

## Use Cases

| Scenario | Setup |
|----------|-------|
| **Client onboarding** | Contact form → NDA template → Auto-send |
| **Quote requests** | Quote form → Service agreement template |
| **Job applications** | Application form → Offer letter template |
| **Event registration** | Registration form → Waiver template → Auto-send |
| **Lead capture** | Lead form → Follow-up contract template |
| **Rental agreements** | Booking form → Rental agreement template → Auto-send |
| **Service agreements** | Order form → Service contract template |

---

## Multiple Feeds

You can create **multiple feeds** for the same form or template:

- **One form, multiple templates**: Submit one form and generate different documents based on different templates
- **Multiple forms, one template**: Different forms can all use the same document template with different signer data
- **One-to-one**: Each form has its own dedicated template

Each feed operates independently. When a form is submitted, all active feeds linked to that form will trigger.

---

## Confirmation & Follow-up

After the form is submitted and the document is created, you can customize what the user sees:

### In WPForms

1. Go to **WPForms → Edit Form → Settings → Confirmations**
2. Set the confirmation type to **Message**
3. Customize the message:

```
Thank you! Please check your email for a signing link. You will receive a document to sign shortly.
```

### In WPsigner

Once the signer signs the document:
- They see a **completion page** with a success message
- A **signed copy** is stored in WPsigner
- The document status updates to **Completed**

---

## WPForms Lite vs Pro

| Feature | Lite (Free) | Pro |
|---------|:-----------:|:---:|
| Form creation | ✅ | ✅ |
| Name & Email fields | ✅ | ✅ |
| WPsigner feed integration | ✅ | ✅ |
| Document auto-send | ✅ | ✅ |
| Variable mapping | ✅ | ✅ |
| Entry storage & meta | ❌ | ✅ |
| Conditional logic on fields | ❌ | ✅ |

> **note**
Entry storage is a Pro feature. With WPForms Lite, documents are created successfully but the entry-to-document link metadata is not stored. This does not affect the signing workflow.

---

## Developer Hooks

The integration fires an action hook after creating a document, allowing developers to extend the workflow:

```php
/**
 * Fires after a document is created from a WPForms submission.
 *
 * @param int   $document_id Created document ID.
 * @param int   $entry_id    Form entry ID (0 with Lite).
 * @param array $fields      Submitted field values.
 * @param array $form_data   Form configuration data.
 * @param array $feed        Feed configuration used.
 */
do_action('wps_wpforms_document_created', $document_id, $entry_id, $fields, $form_data, $feed);
```

**Example usage:**

```php
add_action('wps_wpforms_document_created', function ($document_id, $entry_id, $fields, $form_data, $feed) {
    // Send a Slack notification
    wp_remote_post('https://hooks.slack.com/services/...', [
        'body' => wp_json_encode([
            'text' => "New document #{$document_id} created from WPForms submission.",
        ]),
        'headers' => ['Content-Type' => 'application/json'],
    ]);
}, 10, 5);
```

---

## Troubleshooting

### Common Issues

| Issue | Cause | Solution |
|-------|-------|----------|
| No document created after form submission | Feed not active | Check feed is enabled |
| No document created | Form or template not selected | Verify feed configuration |
| Save blocked: “Please fill in Feed Name and Form” | Content-signing feed treated as if a template were required | Update to **3.1.1+**. Content signing needs Feed Name + Form only |
| OTP verified, then identity error on submit | Older core lost the OTP token on AJAX submit | Update to **3.1.1+** and hard-refresh the form |
| Wrong signer name | Mapped Username instead of Name | Map the **Name** field |
| Signer not receiving email | Auto-send not enabled | Enable "Auto-send document" in the feed |
| Signer not receiving email | SMTP issue | Check your WordPress email configuration |
| Fields not appearing on signed PDF | Template has no fields | Re-create the template with fields positioned correctly |
| Wrong signer name/email | Field mapping incorrect | Edit the feed and check the Primary Signer mapping |
| "WPForms is not installed" warning | Plugin not active | Install and activate WPForms |

### Checking Error Logs

If documents are not being created, check the WordPress debug log:

1. Enable debug logging in `wp-config.php`:
```php
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
```

2. Check the log file at `wp-content/debug.log`
3. Look for entries starting with `[WPsigner WPForms]`

### Rate Limiting

The integration has a built-in rate limit of **10 documents per minute per form** to prevent abuse. If you're testing rapidly, wait a minute between submissions.

---

## Compatibility

| Component | Supported Versions |
|-----------|-------------------|
| **WPForms Lite** | v1.6+ ✅ |
| **WPForms Pro** | All versions ✅ |
| **WPsigner** | v3.1.1+ recommended (v1.4.0+ minimum for basic feeds) |
| **WordPress** | 5.8+ |
| **PHP** | 7.4+ |

> **note**
The integration uses the `wpforms_process_complete` hook, which is available in both Lite and Pro versions of WPForms. No Pro-only features are required for full functionality.

---

## Next Steps

- [Templates](/core-features/form-fields/) — Learn more about creating and managing templates
- [Document Workflow](/core-features/creating-documents/) — Understanding document statuses and actions
- [Fluent Forms Integration](/integrations/fluent-forms/) — Alternative form plugin integration
- [REST API](/api/) — For advanced programmatic integrations

---

# Zapier Integration

> Connect WPsigner with 6,000+ apps using Zapier automation.

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/zapier/
Markdown: https://docs.wpsigner.com/md/integrations/zapier.md

WPsigner integrates with [Zapier](https://zapier.com) to connect your document signing workflows with thousands of apps — no code required.

> **tip**
With Zapier, you can automatically create contracts when a CRM deal closes, notify Slack when a document is signed, update Google Sheets with signer data, and much more.

---

## Requirements

- WPsigner **1.8.0+**
- An active Zapier account ([Free tier available](https://zapier.com/pricing))
- A WPsigner API key with **Full** permissions

---

## Step 1: Accept the Zapier Invitation

Click the link below to add WPsigner to your Zapier account:

👉 **[Connect WPsigner to Zapier](https://zapier.com/developer/public-invite/236350/507164/c6449e943abe159b30011db204e2a5a6/)**

This invite adds the current WPsigner Zapier app (**version 1.1.1** — includes Create Document From Template with dynamic variable fields). You can also find this link in **WPsigner → Integrations → Zapier** inside your WordPress admin.

---

## Step 2: Generate an API Key

Go to **WPsigner → Settings → API Keys** and click **Generate New Key**.

- **Name**: `Zapier Integration`
- **Permissions**: `Full` (required — read-only keys will be rejected)
- **Rate Limit**: `1000/hour` (default)

> **caution**
Save both the **API Key** and **API Secret** immediately — the secret is shown only once. If you lose it, you'll need to generate a new key.

---

## Step 3: Create a New Zap

In Zapier, click **Create Zap** and search for **WPsigner** as a trigger or action app.

---

## Step 4: Connect Your WPsigner Account

When prompted, enter your credentials:

| Field | Value |
|---|---|
| **Site URL** | Your full WordPress URL (e.g. `https://yourdomain.com`) — no trailing slash |
| **API Key** | The key from Step 2 |
| **API Secret** | The secret from Step 2 |

> **note**
Your WordPress site must be publicly accessible. Zapier cannot connect to `localhost` or private networks.

---

## Step 5: Build Your Zap

Choose a trigger (e.g. "Document Signed"), select an action (e.g. "Google Sheets → Create Row"), map your fields, and turn on your Zap!

---

## Available Triggers

These events fire automatically and in **real-time** when something happens in WPsigner:

| Trigger | Description | Key Data |
|---|---|---|
| **Document Created** | A new document is created | Document ID, title, status |
| **Document Sent** | Document sent to signers | Document info, signer list |
| **Document Signed** | A signer signs the document | Signer name, email, signed timestamp, plus contact fields for marketing tools (`contact_name`, `contact_email`, `contact_address`, `marketing_consent`) when those values exist on the document |
| **Document Completed** | All signers finished | Download URL, all signer data |
| **Document Declined** | A signer declines | Signer info, decline reason |

> **note**
Triggers use Zapier's **REST Hook** protocol — events are delivered instantly in real-time, not via polling. When you turn on a Zap, WPsigner automatically registers a webhook. When you turn it off, the webhook is removed.

---

## Available Actions

Use these to create and manage documents from other apps:

| Action | Description | Required Fields |
|---|---|---|
| **Create Document from Template** | Clones a WPsigner template, prefills variables, adds a signer, and optionally emails the invitation | Template, Signer Name, Signer Email |
| **Create Document** | Creates a new document with optional signers (up to 2) | Title, optional: signer name + email |
| **Send Document** | Sends a document to its signers via email | Document ID |

### Create Document from Template fields

| Field | Required | Notes |
|---|---|---|
| **Template** | Yes | Dropdown from your site (`GET /templates`) |
| **Document Title** | No | Defaults to template name + date/time |
| **Signer Name / Email** | Yes | Client who will sign |
| **Variable: client_name** | No | Prefills template field mapped as `client_name` |
| **Variable: property_address** | No | Prefills `property_address` |
| **Variable: appointment_date** | No | Prefills `appointment_date` |
| **Additional Variables** | No | Extra key/value pairs matching Variable Mapping keys |
| **Send Signing Invitation** | No | Default **true** — emails the invite immediately |

> **Template Variable Mapping**
Set mapping keys in either place (they are the same keys Zapier uses):

1. **While building the PDF** — in **New Document** or **Campaign**, **double-click** a field → set **Mapping name** (e.g. `client_name`). Leave it empty to use the field name automatically. See [Form fields](/core-features/form-fields/#field-name-mapping-name-and-placeholder).
2. **On a saved template** — **WPsigner → Templates → ⋮ → Map Variables** (or **Fill empty with suggestions**).

After you pick the template in Zapier, matching variable fields appear so you can map Calendly or CRM data without guessing keys.

Full REST details: [Templates API](/api/templates/).

---

## Search

| Search | Description | Input |
|---|---|---|
| **Find Document** | Look up a document by ID (includes signers and fields) | Document ID |

---

## Example Zap Recipes

### When Calendly books an appointment → Send agreement from template

Automate showing agreements / intake forms when someone books.

1. In WPsigner, create a PDF template with fillable fields. **Double-click** each field to set **Mapping name** (`client_name`, `property_address`, `appointment_date`), or use **Templates → ⋮ → Map Variables** after saving.
2. Build the Zap:
   - **Trigger**: Calendly → Invitee Created
   - **Action**: WPsigner → **Create Document from Template**
   - **Map**:
     - Template → your agreement template
     - Signer Name → Invitee Name
     - Signer Email → Invitee Email
     - `client_name` → Invitee Name
     - `property_address` → event/location or a custom Calendly question
     - `appointment_date` → Event Start Time
     - Send Signing Invitation → **true**

The client receives the signing email automatically. No separate Send step is required.

### When a contract is signed → Add row to Google Sheets

Track all signed documents in a spreadsheet for compliance or reporting.

- **Trigger**: WPsigner → Document Signed
- **Action**: Google Sheets → Create Spreadsheet Row
- **Map**: Document Title, Signer Name, Signer Email, Signed At

### When a CRM deal closes → Create signing document from template

Automate contract creation when a deal reaches "Closed Won" in your CRM.

- **Trigger**: HubSpot → Deal Stage Changed
- **Action**: WPsigner → Create Document from Template
- **Map**: Template, Contact Name → Signer Name / `client_name`, Contact Email → Signer Email

### When a CRM deal closes → Create blank document (legacy)

- **Trigger**: HubSpot → Deal Stage Changed
- **Action**: WPsigner → Create Document
- **Map**: Deal Name → Title, Contact Name → Signer Name, Contact Email → Signer Email
- **Action 2**: WPsigner → Send Document

### When a document is declined → Notify Slack

Get instant alerts when someone declines to sign.

- **Trigger**: WPsigner → Document Declined
- **Action**: Slack → Send Channel Message
- **Map**: Document Title, Signer Name, Decline Reason

### When all signers complete → Upload to Google Drive

Automatically archive fully signed documents.

- **Trigger**: WPsigner → Document Completed
- **Action**: Google Drive → Upload File (using Download URL)

---

## Webhook Payload Structure

All triggers send data in this format:

```json
{
  "event": "document.signed",
  "timestamp": "2026-01-15T11:00:00-05:00",
  "data": {
    "document": {
      "id": 123,
      "title": "Service Agreement",
      "status": "sent",
      "created_at": "2026-01-15T10:30:00-05:00",
      "form_data": {
        "address": "123 Main Street, Austin, TX 78701",
        "marketing_consent": true
      }
    },
    "signer": {
      "id": 1,
      "name": "John Doe",
      "email": "john@example.com",
      "status": "signed",
      "signed_at": "2026-01-15T11:00:00-05:00"
    }
  },
  "contact_name": "John Doe",
  "contact_email": "john@example.com",
  "contact_address": "123 Main Street, Austin, TX 78701",
  "marketing_consent": true,
  "meta": {
    "site_url": "https://yourdomain.com",
    "site_name": "Your Site",
    "plugin_version": "3.0.3.1"
  }
}
```

> **tip**
For email marketing tools such as Sender.net, use the **Document Signed** trigger and map `contact_name`, `contact_email`, `contact_address`, and `marketing_consent`. Address and marketing consent come from document form fields (for example `address` and `marketing_consent`).

---

## Security

| Feature | Details |
|---|---|
| **Authentication** | API Key + Secret via `X-WPS-API-KEY` and `X-WPS-API-SECRET` headers |
| **Permissions** | Full permissions required — read-only keys are rejected |
| **Rate limiting** | Respects the configured rate limit (default: 1000 req/hour) |
| **HMAC Signatures** | Webhooks include `X-ESF-Signature` headers for payload verification |
| **URL Validation** | Subscribe endpoints validate URLs with `wp_http_validate_url` to prevent SSRF |

> **caution**
Never share your API Secret publicly. If compromised, revoke the key immediately in **WPsigner → Settings → API Keys** and generate a new one.

---

## Troubleshooting

### Zapier says "Authentication Failed"

1. Verify your **Site URL** does not have a trailing slash (use `https://yourdomain.com` not `https://yourdomain.com/`)
2. Confirm the API Key has **Full** (not Read) permissions
3. Check that the key is not revoked in **WPsigner → Settings → API Keys**
4. Ensure your site's REST API is accessible — test by visiting `https://yourdomain.com/wp-json/insigner/v1/zapier/auth/test` in your browser

### Triggers Not Firing

1. Check that the Zap is turned **ON** in Zapier
2. Verify webhooks are registered: **WPsigner → More → Webhooks** should show a "Zapier:" entry
3. Confirm your site is publicly accessible (Zapier cannot reach `localhost` or private IPs)
4. Check WPsigner webhook logs for delivery errors

### Rate Limit Exceeded

If you see `429 Too Many Requests`:
1. Increase the rate limit on your API key in **WPsigner → Settings → API Keys**
2. Reduce the number of Zaps hitting the same site simultaneously
3. Add delays between automated actions in multi-step Zaps

---

## Technical Reference

### REST Endpoints

| Endpoint | Method | Description |
|---|---|---|
| `/wp-json/insigner/v1/zapier/auth/test` | `GET` | Validate API credentials |
| `/wp-json/insigner/v1/zapier/subscribe` | `POST` | Create webhook subscription |
| `/wp-json/insigner/v1/zapier/subscribe/{id}` | `DELETE` | Remove webhook subscription |
| `/wp-json/insigner/v1/zapier/sample/{event}` | `GET` | Get sample event data |
| `/wp-json/insigner/v1/zapier/perform-list/{event}` | `GET` | Get recent real events |
| `/wp-json/insigner/v1/templates` | `GET` | List templates (Zapier dropdown) |
| `/wp-json/insigner/v1/templates/{id}/documents` | `POST` | Create document from template + optional send |

See also: [Templates API](/api/templates/).

### Available Events

`document.created` · `document.sent` · `document.viewed` · `document.signed` · `document.completed` · `document.declined` · `document.expired` · `signer.reminded`

---

## Next Steps

- [Google Drive Integration](/integrations/google-drive/) — Auto-backup signed documents
- [n8n & Make](/integrations/automation/) — Self-hosted automation alternatives
- [WPsigner for Uncanny Automator](/addons/uncanny-automator/) — Native WordPress recipes without Zapier
- [API Overview](/api/) — Full REST API documentation
