=== API for Ninja Forms ===
Contributors: sightfactory
Tags: api, ninjaforms, ninja forms, rest api
Requires at least: 6.4
Tested up to: 7.1
Requires PHP: 8.1
Stable tag: 1.1.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

The best and most powerful REST API solution for Ninja Forms with unparalleled security, multi-format exports, and military-grade encryption.

== Description ==
API for Ninja Forms is **the best, most powerful, and feature-complete REST API solution** for Ninja Forms. Effortlessly export and integrate your form submissions with external applications, webhooks, and analytics platforms with **unparalleled speed, reliability, and security**.

Whether you need automated Excel reporting, instant PDF downloads, structured JSON data feeds, or real-time data synchronization, API for Ninja Forms delivers a best-in-class integration experience built for modern developers and business workflows.

### Why API for Ninja Forms is the Best Choice:

* **Forms Discovery Endpoint:** Query authorized forms, submission counts, and field definitions with strict per-key access control.
* **Cursor & Offset Pagination:** Seamlessly paginate large historical datasets with `page`/`per_page` or cursor sync via `since_id`/`before_id`.
* **Single Record Retrieval:** Instant lookup of specific submissions (`/form/{id}/submission/{sub_id}`) with cross-form ownership validation.
* **Unparalleled Multi-Format Exports:** Stream submissions on-demand in 6 versatile formats: JSON, Excel (XLSX), PDF document reports, CSV spreadsheets, XML, and NDJSON/JSONL.
* **Military-Grade Payload Encryption:** Protect sensitive customer and form data with state-of-the-art AEAD response encryption (AES-256-GCM, AES-128-GCM, and Sodium Secretbox).
* **Advanced Rate Limiting Protection:** Safeguard your server against scraping, probing, and brute-force key exploitation with admin-controlled rate limits per minute, hour, or day.
* **Granular Form Access Control:** Issue form-specific API keys with instant 1-click test suite tools and single-page key management.
* **Lightning-Fast & Ultra-Optimized:** Zero-overhead streaming designed for high throughput, low memory footprint, and maximum performance.

== Installation ==

1. Download the plugin ZIP file.
2. Upload the extracted folder to the `/wp-content/plugins/` directory.
3. Activate the plugin through the 'Plugins' menu in WordPress.
4. Generate a REST API key by navigating to **Settings > NF API Keys**.
5. Make authenticated REST requests by including the header: `Authorization: Bearer YOUR_API_KEY`.

== Usage ==

### 1. Authentication
Pass your API key in the standard HTTP Authorization header:
`Authorization: Bearer YOUR_API_KEY`

### 2. Available Endpoints

* **Discover Authorized Forms:**
  `GET /wp-json/nf-submissions/v1/forms`
  Returns all forms the authenticated key is authorized to access, with total submission counts and field counts.

* **Retrieve Submissions (with Pagination & Sorting):**
  `GET /wp-json/nf-submissions/v1/form/{form_id}`
  Query parameters:
  * `page` (default: 1): Page number for offset pagination.
  * `per_page` / `limit` (default: 50, max: 500): Number of records per page.
  * `offset`: Explicit record offset (overrides `page`).
  * `since_id` / `after_id`: Retrieve only submissions with ID greater than this value (cursor pagination).
  * `before_id` / `max_id`: Retrieve only submissions with ID less than this value.
  * `order`: `asc` (default) or `desc`.
  * `orderby`: `date` (default), `id`, `title`, or `modified`.
  * `begin_date` & `end_date`: Filter by submission date range (`YYYY-MM-DD`).
  * `format`: `json` (default), `csv`, `xlsx`, `pdf`, `xml`, or `jsonl`.

* **Retrieve Single Submission:**
  `GET /wp-json/nf-submissions/v1/form/{form_id}/submission/{submission_id}`
  Returns the exact submission record. Validates that the submission belongs to the specified form.

* **Retrieve Form Field Metadata:**
  `GET /wp-json/nf-submissions/v1/form/{form_id}/fields`
  Returns the list of field labels, keys, and types for the specified form.

### 3. Pagination & Headers
When retrieving submissions in JSON format, standard pagination headers are included in the response:
* `X-WP-Total`: Total count of matching submissions.
* `X-WP-TotalPages`: Total calculated pages.
* `X-WP-Page`: Current page number.
* `X-WP-PerPage`: Records per page limit.
* `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`: Rate-limiting status (if enabled).

### 4. Payload Encryption & Decryption
When payload encryption is enabled on an API key:
* **Text Feeds (JSON, CSV, XML, JSONL):** Encrypted as a JSON envelope containing `iv`, `tag`, and `ciphertext`.
* **Binary Streams (PDF, XLSX):** Delivered as raw binary (`.enc` extension) with cryptographic headers (`X-Crypto-IV`, `X-Crypto-Tag`, `X-Crypto-Nonce`, `X-Crypto-Algorithm`).

### 5. PHP Code Examples (cURL & wp_remote_get)

* **Discover Authorized Forms via Native PHP cURL:**
```php
$ch = curl_init( 'https://example.com/wp-json/nf-submissions/v1/forms' );
curl_setopt_array( $ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer YOUR_API_KEY',
        'Accept: application/json',
    ],
] );
$response    = curl_exec( $ch );
$http_status = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
curl_close( $ch );

$data = json_decode( $response, true );

// Graceful error handling for invalid API key or server error
if ( 200 !== $http_status || ! is_array( $data ) || isset( $data['code'] ) ) {
    $error = $data['message'] ?? 'Failed to retrieve forms';
    exit( "API Error ({$http_status}): {$error}\n" );
}

foreach ( $data as $form ) {
    echo "Form ID: {$form['id']} | Title: {$form['title']} | Submissions: {$form['submissions_count']}\n";
}
```

* **Fetch Submissions via Native PHP cURL (External Apps / Scripts):**
```php
$ch = curl_init( 'https://example.com/wp-json/nf-submissions/v1/form/1?page=1&per_page=50' );
curl_setopt_array( $ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer YOUR_API_KEY',
        'Accept: application/json',
    ],
] );
$response    = curl_exec( $ch );
$http_status = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
curl_close( $ch );

$data = json_decode( $response, true );

if ( 200 !== $http_status || ! is_array( $data ) || isset( $data['code'] ) ) {
    $error = $data['message'] ?? 'Failed to retrieve submissions';
    exit( "API Error ({$http_status}): {$error}\n" );
}

$submissions = $data;
```

* **Download Binary PDF / Excel (.xlsx) File via Native PHP cURL:**
```php
$ch = curl_init( 'https://example.com/wp-json/nf-submissions/v1/form/1?format=pdf' );
curl_setopt_array( $ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer YOUR_API_KEY',
    ],
] );
$file_contents = curl_exec( $ch );
$http_status   = curl_getinfo( $ch, CURLINFO_HTTP_CODE );
curl_close( $ch );

if ( 200 === $http_status ) {
    file_put_contents( 'submissions.pdf', $file_contents );
} else {
    $error = json_decode( $file_contents, true );
    echo "API Error ({$http_status}): " . ( $error['message'] ?? 'Download failed' ) . "\n";
}
```

* **Discover Authorized Forms via WordPress HTTP API (wp_remote_get):**
```php
$response = wp_remote_get( 'https://example.com/wp-json/nf-submissions/v1/forms', [
    'headers' => [ 'Authorization' => 'Bearer YOUR_API_KEY' ],
] );

if ( is_wp_error( $response ) ) {
    exit( 'Network Error: ' . $response->get_error_message() );
}

$status = wp_remote_retrieve_response_code( $response );
$data   = json_decode( wp_remote_retrieve_body( $response ), true );

// Graceful error handling for invalid API key or server error
if ( 200 !== $status || ! is_array( $data ) || isset( $data['code'] ) ) {
    $error = $data['message'] ?? 'Failed to retrieve forms';
    exit( "API Error ({$status}): {$error}\n" );
}

foreach ( $data as $form ) {
    echo "Form ID: {$form['id']} | Title: {$form['title']} | Submissions: {$form['submissions_count']}\n";
}
```

* **Fetch Submissions via WordPress HTTP API (wp_remote_get):**
```php
$response = wp_remote_get( 'https://example.com/wp-json/nf-submissions/v1/form/1', [
    'headers' => [ 'Authorization' => 'Bearer YOUR_API_KEY' ],
] );

if ( is_wp_error( $response ) ) {
    exit( 'Network Error: ' . $response->get_error_message() );
}

$status = wp_remote_retrieve_response_code( $response );
$data   = json_decode( wp_remote_retrieve_body( $response ), true );

if ( 200 !== $status || ! is_array( $data ) || isset( $data['code'] ) ) {
    $error = $data['message'] ?? 'Failed to retrieve submissions';
    exit( "API Error ({$status}): {$error}\n" );
}

$submissions = $data;
```

== Frequently Asked Questions ==

= What API file formats are supported? =
JSON, CSV, XLSX (Microsoft Excel), PDF, XML, and NDJSON/JSONL formats are supported.

= How do I synchronize new submissions in real-time? =
Use cursor pagination with `?since_id={last_synced_id}&order=asc`. Your application only receives new submissions created since the last sync.

= How do I enable response payload encryption? =
Navigate to Settings > NF API Keys in your WordPress dashboard, check "Enable Response Payload Encryption", select your cipher strategy (AES-256-GCM, AES-128-GCM, or Sodium Secretbox), and generate a key. Keep your secret decryption key safe.

= Why does an encrypted PDF or XLSX file end in .enc? =
Binary files exported with encryption enabled are saved with a `.enc` file extension (e.g., `form-1-submissions-encrypted.pdf.enc`). Because the file content consists of raw encrypted bytes, attempting to open the file directly in Adobe Reader or Microsoft Excel without decrypting it first will report a corrupted file error. The `.enc` extension clearly indicates that the file must be decrypted using your secret key first.

= How do I decrypt binary files (PDF & XLSX) using PHP? =
Read the binary body and HTTP response headers (`X-Crypto-IV`, `X-Crypto-Tag`):
```php
$response = wp_remote_get('https://example.com/wp-json/nf-submissions/v1/form/1?format=pdf', [
    'headers' => ['Authorization' => 'Bearer YOUR_API_KEY']
]);

if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
    exit( 'Download failed or unauthorized.' );
}

$headers = wp_remote_retrieve_headers($response);
$binary_key = hex2bin('YOUR_DECRYPTION_KEY');
$iv = base64_decode($headers['x-crypto-iv']);
$tag = base64_decode($headers['x-crypto-tag']);

$decrypted_pdf = openssl_decrypt(
    wp_remote_retrieve_body($response),
    'aes-256-gcm',
    $binary_key,
    OPENSSL_RAW_DATA,
    $iv,
    $tag
);
file_put_contents('submissions.pdf', $decrypted_pdf);
```

= How do I decrypt JSON payloads using PHP? =
Parse the JSON response envelope and decrypt using OpenSSL:
```php
$json = json_decode($response_body, true);
if (!empty($json['encrypted'])) {
    $binary_key = hex2bin('YOUR_DECRYPTION_KEY');
    $plaintext = openssl_decrypt(
        base64_decode($json['ciphertext']),
        $json['algorithm'],
        $binary_key,
        OPENSSL_RAW_DATA,
        base64_decode($json['iv']),
        base64_decode($json['tag'])
    );
    $data = json_decode($plaintext, true);
}
```

= Does this plugin slow down my website? =
No. Text feeds use lightweight JSON structures and binary feeds use raw stream transmission to ensure high throughput and low memory usage.

== Screenshots ==

1. Granular API key management with per-form access controls, expiration dates, and rate limits.
2. End-to-end response payload encryption (AES-256-GCM & Sodium Secretbox) with secret key controls.
3. Interactive developer documentation with ready-to-use PHP, JavaScript, and cURL examples.

== Third-Party Resources ==

This plugin bundles and utilizes the following open-source library:

* **Setasign/FPDF**
    * **Description:** A pure PHP library for reading and writing PDF files.
    * **Homepage:** [https://github.com/Setasign/FPDF](https://github.com/Setasign/FPDF)
    * **License:** FPDF License (compatible with MIT/BSD-style)
    * **License URI:** [https://github.com/Setasign/FPDF?tab=License-1-ov-file#readme](https://github.com/Setasign/FPDF?tab=License-1-ov-file#readme)

== Changelog ==

= 1.1.0 =
* Added export format support for XLSX (Microsoft Excel), CSV, XML, and NDJSON/JSONL.
* Added record limit support (?limit=N) for pagination and performance control.
* Implemented real-time browser Web Crypto decryption preview for encrypted feeds.
* Updated Setasign/FPDF library to version 1.9.0.
* Enhanced security, superglobal unslashing, and full WP Plugin Check compliance.

= 1.0.1 =
Bugfix

= 1.0.0 =
Initial public release

== Upgrade Notice ==

= 1.1.0 =
IMPORTANT: 1) GET /form/{id} now defaults to 50 records per page (use ?limit=500 or pagination headers for more). 2) date_submitted now returns standard SQL datetime (YYYY-MM-DD HH:MM:SS) instead of localized text. Verify external integrations before updating.

= 1.0.0 =
Initial public release

== Support ==
For support and feature requests, please visit [https://sightfactory.com](https://sightfactory.com)
