diff --git a/.github/workflows/gitleaks.yml b/.github/workflows/gitleaks.yml new file mode 100644 index 0000000..d49e2c4 --- /dev/null +++ b/.github/workflows/gitleaks.yml @@ -0,0 +1,13 @@ +name: Gitleaks + +on: [push,pull_request] + +jobs: + gitleaks: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: gitleaks-action + uses: Nitro/gitleaks-action@master + # This uses our fork of the action + \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a6fee4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,18 @@ +# Environment variables +.env + +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python + +# OS +.DS_Store + +# IDE +.vscode/ +.idea/ +*.swp +*.swo diff --git a/README.md b/README.md index e1d3390..8675a33 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,72 @@ -# nitro-platform-integrations -Examples and tools for integrating with Nitro Platform API (document conversion, extraction, transformation) +# Nitro Platform API Integrations + +This repository provides examples and tools for integrating with the Nitro Platform API, enabling document conversion, extraction, transformation, and workflow automation. + +## What is the Platform API? + +The Nitro Platform API provides programmatic access to document processing capabilities including: +- **Document Conversions**: Convert between formats (Word↔PDF, Excel→PDF, PowerPoint→PDF, etc.) +- **Data Extraction**: Extract text, form data, tables, and detect PII from documents +- **Document Transformations**: Compress, merge, split, redact, password protect, and more +- **Async Processing**: Handle large documents with job-based workflows + +## Repository Structure + +``` +├── postman/ # Postman collection and environment +├── power-automate/ # Custom connector and sample flows +├── samples/ +│ └── python/ # Python examples +└── docs/ # API documentation and guides +``` + +## Getting Started + +### Prerequisites + +1. **API Access**: Get OAuth2 client credentials: + - Go to [https://admin.gonitro.com](https://admin.gonitro.com) + - Navigate to **Settings** → **API** + - Click **Create Application** + - Name your application and save the Client ID and Client Secret + +2. **Environment Setup**: Set the following variables: + ```bash + export PLATFORM_BASE_URL=https://api.gonitro.dev + export PLATFORM_CLIENT_ID= + export PLATFORM_CLIENT_SECRET= + ``` + +### Quick Start Options + +#### Using Postman +1. Import the collection from `postman/Platform-API.postman_collection.json` +2. Configure environment variables (see `postman/README.md`) +3. Get a bearer token and run sample requests + +#### Using Power Automate +1. Create a custom connector using instructions in `power-automate/` +2. Configure API key authentication +3. Build flows for document processing workflows + +#### Using Sample Code +- **Python**: See `samples/python/README.md` + +## Authentication + +The API uses OAuth2 authentication: +1. Exchange client credentials for an access token via `POST /oauth/token` +2. Include the token in requests: `Authorization: Bearer ` + +See the [authentication documentation](https://developers.gonitro.com/docs/api-reference/authentication/get-access-token) for details. + +## Common Workflows + +- **Document Conversion**: Upload a file → Get converted result +- **Async Processing**: Start job → Poll status → Download result +- **Data Extraction**: Upload PDF → Extract text/tables/forms +- **Document Transformation**: Upload PDF → Apply operations → Download result + +## Support + +For API documentation and support, visit the [developer portal](https://developers.gonitro.dev). diff --git a/docs/API-overview.md b/docs/API-overview.md new file mode 100644 index 0000000..c4b29a5 --- /dev/null +++ b/docs/API-overview.md @@ -0,0 +1,74 @@ +# API Overview + +The Nitro Platform API provides document processing capabilities through a RESTful interface. + +## Base URL + +All API requests are made to: `https://api.gonitro.dev` + +## Authentication + +The API uses OAuth2 client credentials flow: + +1. **Get Credentials**: Create an application at [admin.gonitro.com](https://admin.gonitro.com) → Settings → API +2. **Get Token**: Exchange credentials for access token via `POST /oauth/token` +3. **Use Token**: Include in requests as `Authorization: Bearer ` + +## Request/Response Patterns + +### Synchronous Operations +- Submit request with file +- Get immediate response with result +- Best for: Small files, simple operations + +### Asynchronous Operations +- Submit request, get job ID +- Poll `/jobs/{jobId}/status` for progress +- Download result when complete +- Best for: Large files, complex operations + +## API Categories + +### Conversions (`/platform/conversions`) +Convert documents between formats: +- Word ↔ PDF +- Excel → PDF +- PowerPoint → PDF +- Images → PDF + +### Extractions (`/platform/extractions`) +Extract data from documents: +- Text extraction +- Form data extraction +- Table data extraction +- PII detection + +### Transformations (`/platform/transformations`) +Modify PDF documents: +- Compress, merge, split +- Redact content +- Password protect/remove +- Rotate/delete pages + +### Jobs (`/jobs`) +Manage asynchronous operations: +- Check job status +- Download job results +- Handle job errors + +## Error Handling + +The API returns standard HTTP status codes: +- `200` - Success +- `400` - Bad Request (invalid parameters) +- `401` - Unauthorized (invalid/missing token) +- `404` - Not Found (invalid endpoint/job ID) +- `500` - Internal Server Error + +Error responses include details: +```json +{ + "error": "Invalid file format", + "message": "Only PDF files are supported for this operation" +} +``` diff --git a/docs/workflow-examples.md b/docs/workflow-examples.md new file mode 100644 index 0000000..e2cf880 --- /dev/null +++ b/docs/workflow-examples.md @@ -0,0 +1,104 @@ +# Workflow Examples + +Common end-to-end workflows using the Platform API. + +## Document Conversion Workflow + +**Goal**: Convert Word documents to PDF + +### Using Postman +1. Run "Get Bearer Token" request +2. Use "Word → PDF" request with your .docx file +3. Download the converted PDF from response + +### Using Python +```python +token = get_access_token(client_id, client_secret) +with open('document.docx', 'rb') as f: + result = convert_to_pdf(token, f) +with open('document.pdf', 'wb') as f: + f.write(result) +``` + +### Using Power Automate +1. **Trigger**: When file added to SharePoint +2. **Action**: Convert using custom connector +3. **Action**: Save result back to SharePoint + +## Data Extraction Workflow + +**Goal**: Extract text and tables from PDF invoices + +### Steps +1. **Upload PDF**: Submit to `/platform/extractions/pdf-text` +2. **Extract Tables**: Submit to `/platform/extractions/pdf-table-data` +3. **Process Data**: Parse extracted JSON for relevant fields +4. **Store Results**: Save to database/spreadsheet + +### Use Cases +- Invoice processing +- Form data extraction +- Document analysis +- Content migration + +## Async Processing Workflow + +**Goal**: Process large documents without timeouts + +### Steps +1. **Submit Job**: POST to conversion/extraction endpoint +2. **Get Job ID**: Note the `jobId` from response +3. **Poll Status**: GET `/jobs/{jobId}/status` until `status: "completed"` +4. **Download Result**: GET `/jobs/{jobId}/result` + +### Implementation Pattern +```python +# Submit job +job_response = submit_conversion_job(token, file_data) +job_id = job_response['jobId'] + +# Poll until complete +while True: + status = get_job_status(token, job_id) + if status['status'] == 'completed': + break + elif status['status'] == 'failed': + raise Exception(f"Job failed: {status['message']}") + time.sleep(5) # Wait 5 seconds before next check + +# Download result +result = get_job_result(token, job_id) +``` + +## Batch Processing Workflow + +**Goal**: Process multiple documents efficiently + +### Strategy +1. **Queue Jobs**: Submit all files as async jobs +2. **Track Progress**: Monitor all job IDs +3. **Download Results**: Collect completed results +4. **Handle Errors**: Retry failed jobs + +### Best Practices +- Use async processing for files > 10MB +- Add delays between API calls (rate limiting) +- Implement retry logic for failed requests +- Log all operations for debugging + +## Document Pipeline Workflow + +**Goal**: Complete document processing pipeline + +### Example: Invoice Processing +1. **Convert**: Word/Excel invoices → PDF +2. **Extract**: Text and table data from PDFs +3. **Validate**: Check extracted data quality +4. **Transform**: Redact sensitive information +5. **Store**: Save processed documents and data + +### Integration Points +- **Input**: SharePoint, OneDrive, email attachments +- **Processing**: Platform API operations +- **Output**: Database, CRM, accounting system +- **Notifications**: Email, Teams, Slack alerts diff --git a/postman/Platform-API.postman_collection.json b/postman/Platform-API.postman_collection.json new file mode 100755 index 0000000..deec692 --- /dev/null +++ b/postman/Platform-API.postman_collection.json @@ -0,0 +1,2133 @@ +{ + "info": { + "_postman_id": "b9794fa2-0560-41b7-ac41-124b067fe372", + "name": "Nitro Automation Platform APIs", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "49176897", + "_collection_link": "https://connective.postman.co/workspace/Personal-Workspace~a573f67d-3b8e-48d5-9420-74f34a276738/collection/49176897-b9794fa2-0560-41b7-ac41-124b067fe372?action=share&source=collection_link&creator=49176897" + }, + "item": [ + { + "name": "Authorization", + "item": [ + { + "name": "Get Bearer Token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "// Parse the response", + "let responseData = pm.response.json();", + "", + "// Store token in an environment variable", + "pm.environment.set(\"token\", responseData.accessToken);", + "", + "// Optional: Log it to the Postman console", + "console.log(\"Token saved:\", responseData.token);" + ], + "type": "text/javascript", + "packages": {}, + "requests": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"clientID\": \"{{clientID}}\",\n \"clientSecret\": \"{{clientSecret}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/oauth/token", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "oauth", + "token" + ] + }, + "description": "Generated from cURL: curl --request POST \\\n --url https://api.gonitro.dev/oauth/token \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"clientID\": \"\",\n \"clientSecret\": \"\"\n}'" + }, + "response": [] + } + ], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Authorization folder: Validate status codes and response structure", + "pm.test(\"Status code is 200 or 201\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201]);", + "});", + "pm.test(\"Response has expected properties\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.be.an('object');", + " pm.expect(jsonData).to.have.any.keys('access_token', 'token_type', 'expires_in');", + "});" + ] + } + } + ] + }, + { + "name": "Conversions", + "item": [ + { + "name": "Word → PDF", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "convert" + }, + { + "key": "params", + "value": "{\"to\": \"pdf\"}" + }, + { + "key": "file", + "type": "file", + "src": "Analysis.docx" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/conversions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "conversions" + ] + }, + "description": "Upload a Word document (.doc, .docx, .dot, .xml, .docm, .dotx, .dotm, .rtf) and convert to PDF." + }, + "response": [] + }, + { + "name": "Excel → PDF", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "convert" + }, + { + "key": "params", + "value": "{\"to\": \"pdf\"}" + }, + { + "key": "file", + "type": "file", + "src": "Feedback.xlsx" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/conversions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "conversions" + ] + }, + "description": "Upload an Excel workbook (.xlsx, .xls, .xlt, .xlsm, .xltx, .xltm) and convert to PDF." + }, + "response": [] + }, + { + "name": "PowerPoint → PDF", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "convert" + }, + { + "key": "params", + "value": "{\"to\": \"pdf\"}" + }, + { + "key": "file", + "value": "", + "type": "file" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/conversions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "conversions" + ] + }, + "description": "Upload a PowerPoint file (.pptx, .ppt) and convert to PDF." + }, + "response": [] + }, + { + "name": "Image → PDF", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "convert" + }, + { + "key": "params", + "value": "{\"to\": \"pdf\"}" + }, + { + "key": "file", + "type": "file", + "src": "GoNitro.png" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/conversions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "conversions" + ] + }, + "description": "Upload an image (.png, .jpg, .jpeg, .tiff) and convert to PDF." + }, + "response": [] + }, + { + "name": "PDF → Word", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "convert" + }, + { + "key": "params", + "value": "{\"to\": \"docx\"}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/conversions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "conversions" + ] + }, + "description": "Upload a PDF and convert to Word (docx)." + }, + "response": [] + }, + { + "name": "PDF → Excel", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "convert" + }, + { + "key": "params", + "value": "{\"to\": \"xlsx\"}" + }, + { + "key": "file", + "type": "file", + "src": "Sample Tables.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/conversions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "conversions" + ] + }, + "description": "Upload a PDF and convert to Excel (xlsx)." + }, + "response": [] + }, + { + "name": "PDF → Image", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "convert" + }, + { + "key": "params", + "value": "{\"to\": \"png\"}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/conversions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "conversions" + ] + }, + "description": "Upload a PDF and convert to image (png/jpg). Modify the `to` field for the desired format." + }, + "response": [] + } + ], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Conversions folder: Validate status codes and response structure", + "pm.test(\"Status code is 200 or 201\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201]);", + "});", + "pm.test(\"Response has expected properties\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.be.an('object');", + " pm.expect(jsonData).to.have.any.keys('jobId', 'status', 'outputUrl');", + "});" + ] + } + } + ] + }, + { + "name": "Extractions", + "item": [ + { + "name": "Extract PDF Text", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "extract-text" + }, + { + "key": "params", + "value": "{}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/extractions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "extractions" + ] + }, + "description": "Extract plain text from the file." + }, + "response": [] + }, + { + "name": "Extract PDF Form Data", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "extract-forms" + }, + { + "key": "params", + "value": "{}" + }, + { + "key": "file", + "type": "file", + "src": "BOB - Student-Loan-Application-Form.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/extractions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "extractions" + ] + }, + "description": "Extract form field data." + }, + "response": [] + }, + { + "name": "Extract PDF Table Data", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "extract-tables" + }, + { + "key": "params", + "value": "{}" + }, + { + "key": "file", + "type": "file", + "src": "Sample Tables.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/extractions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "extractions" + ] + }, + "description": "Extract tabular data." + }, + "response": [] + }, + { + "name": "Extract and Autodetect Bounding Boxes for PII", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "pii-bbox-extraction" + }, + { + "key": "params", + "value": "{\"language\":\"en\"}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/extractions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "extractions" + ] + }, + "description": "Detect PII and return bounding boxes." + }, + "response": [] + }, + { + "name": "Extract Bounding Boxes for Strings", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "text-bbox-extraction" + }, + { + "key": "params", + "value": "{ \"texts\": [ \"Professional\", \"Location\" ]}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/extractions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "extractions" + ] + }, + "description": "Return coordinates for given strings." + }, + "response": [] + }, + { + "name": "Get PDF Properties", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "get-properties" + }, + { + "key": "params", + "value": "{}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/extractions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "extractions" + ] + }, + "description": "Return document properties/metadata." + }, + "response": [] + }, + { + "name": "Set PDF Properties", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "set-properties" + }, + { + "key": "params", + "value": "{\"title\": \"the-title\"}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Set document properties/metadata." + }, + "response": [] + } + ], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Extractions folder: Validate status codes and response structure", + "pm.test(\"Status code is 200 or 201\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201]);", + "});", + "pm.test(\"Response has expected properties\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.be.an('object');", + " pm.expect(jsonData).to.have.any.keys('extractedData', 'status');", + "});" + ] + } + } + ] + }, + { + "name": "Transformations", + "item": [ + { + "name": "Redact Bounding boxes (scrub PII)", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "redact" + }, + { + "key": "params", + "value": "{\n \"redactions\": [\n {\n \"pageIndex\": 0,\n \"boundingBox\": [\n 0.181746726,\n 0.170494927,\n 0.308388903,\n 0.188994937\n ],\n \"PIIType\": \"PHONE\",\n \"confidence\": 0.99\n },\n {\n \"pageIndex\": 0,\n \"boundingBox\": [\n 0.117687911,\n 0.106075797,\n 0.234256545,\n 0.124575798\n ],\n \"PIIType\": \"NAME\",\n \"confidence\": 0.99\n }\n ]\n}\n" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Redact sensitive data from documents." + }, + "response": [] + }, + { + "name": "Compress", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "compress" + }, + { + "key": "params", + "value": "{\"level\": 2}" + }, + { + "key": "file", + "type": "file", + "src": "BOB - Student-Loan-Application-Form.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Compress the PDF size." + }, + "response": [] + }, + { + "name": "Flatten", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "flatten" + }, + { + "key": "params", + "value": "{}" + }, + { + "key": "file", + "type": "file", + "src": "Sample Tables.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Flatten annotations/form fields." + }, + "response": [] + }, + { + "name": "Rotate Pages", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "rotate" + }, + { + "key": "params", + "value": "{ \"rotations\": [ { \"page_index\": 0, \"amount\": 90 }, { \"page_index\": 1, \"amount\": -90 } ]}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Rotate pages by angle." + }, + "response": [] + }, + { + "name": "Delete Pages", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "delete-pages" + }, + { + "key": "params", + "value": "{\n\t\"pageIndices\": [\n\t\t0,\n\t\t2\n\t]\n}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Delete specific pages." + }, + "response": [] + }, + { + "name": "Split", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "split" + }, + { + "key": "params", + "value": "{ \"pageIndices\": [ [0, 2], [1, 3, 4] ]}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Split a document into parts." + }, + "response": [] + }, + { + "name": "Merge", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "merge" + }, + { + "key": "params", + "value": "{}" + }, + { + "key": "file", + "type": "file", + "src": [ + "Sample Tables.pdf", + "SampleResume.pdf" + ] + }, + { + "key": "file2", + "type": "file", + "src": [] + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Merge multiple documents together." + }, + "response": [] + }, + { + "name": "Password Protect", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "protect" + }, + { + "key": "params", + "value": "{\"ownerPassword\": \"secret\", \"userPassword\": \"open\"}" + }, + { + "key": "file", + "type": "file", + "src": "SampleResume.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Add password protection." + }, + "response": [] + }, + { + "name": "Password Remove", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "unprotect" + }, + { + "key": "params", + "value": "{\"ownerPassword\": \"secret\"}" + }, + { + "key": "file", + "type": "file", + "src": "PDF-withpassword.pdf" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Remove password protection." + }, + "response": [] + } + ], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Transformations folder: Validate status codes and response structure", + "pm.test(\"Status code is 200 or 201\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201]);", + "});", + "pm.test(\"Response has expected properties\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.be.an('object');", + " pm.expect(jsonData).to.have.any.keys('jobId', 'status', 'transformedData');", + "});" + ] + } + } + ] + }, + { + "name": "Jobs", + "item": [ + { + "name": "Get Async Job Status", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{baseUrl}}/jobs/{{jobId}}/status", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "jobs", + "{{jobId}}", + "status" + ] + }, + "description": "Fetch status for an asynchronous job by ID." + }, + "response": [] + }, + { + "name": "Get Async Job Result", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + } + ], + "url": { + "raw": "{{baseUrl}}/jobs/{{jobId}}/result", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "jobs", + "{{jobId}}", + "result" + ] + }, + "description": "Retrieve the result for an asynchronous job by ID." + }, + "response": [] + } + ], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Jobs folder: Validate status codes and response structure", + "pm.test(\"Status code is 200 or 201\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201]);", + "});", + "pm.test(\"Response has expected properties\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.be.an('object');", + " pm.expect(jsonData).to.have.any.keys('jobId', 'status', 'createdAt');", + "});" + ] + } + } + ] + }, + { + "name": "Work in Progress", + "item": [ + { + "name": "ToDo - Add Accessibility (Alt Text)", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "add-accessibility" + }, + { + "key": "params", + "value": "{\"altText\": [{\"page\": 1, \"objectId\": \"img1\", \"text\": \"Chart of sales\"}]}" + }, + { + "key": "delivery", + "value": "" + }, + { + "key": "file", + "value": "", + "type": "file" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Add accessibility metadata such as alt text for images." + }, + "response": [] + }, + { + "name": "ToDo - Quick Sign", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "sign" + }, + { + "key": "params", + "value": "{\"signature\": \"\"}" + }, + { + "key": "delivery", + "value": "" + }, + { + "key": "file", + "value": "", + "type": "file" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Apply a simple e-signature." + }, + "response": [] + }, + { + "name": "ToDo - Fill Form", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "fill" + }, + { + "key": "params", + "value": "{\"fields\": {\"Name\": \"Ada Lovelace\"}}" + }, + { + "key": "delivery", + "value": "" + }, + { + "key": "file", + "value": "", + "type": "file" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Programmatically fill PDF forms." + }, + "response": [] + }, + { + "name": "ToDo - Find & Replace Text", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "find-and-replace" + }, + { + "key": "params", + "value": "{\"find\": \"foo\", \"replace\": \"bar\"}" + }, + { + "key": "delivery", + "value": "" + }, + { + "key": "file", + "value": "", + "type": "file" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Find and replace text." + }, + "response": [] + }, + { + "name": "ToDo - Apply OCR", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "ocr" + }, + { + "key": "params", + "value": "{\"lang\": \"eng\"}" + }, + { + "key": "delivery", + "value": "" + }, + { + "key": "file", + "value": "", + "type": "file" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Apply OCR to scanned documents." + }, + "response": [] + }, + { + "name": "X HTML → PDF", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "convert" + }, + { + "key": "params", + "value": "{\"to\": \"pdf\"}" + }, + { + "key": "file", + "type": "file", + "src": "Analysis.html" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/conversions", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "conversions" + ] + }, + "description": "Upload an HTML file or URL JSON to convert to PDF. For remote HTML, set file content-type to application/vnd.gonitro.url+json with {\"url\": \"https://example.com\"}." + }, + "response": [] + }, + { + "name": "Deprecated - Linearize", + "protocolProfileBehavior": { + "followRedirects": true, + "disableUrlEncoding": false, + "disableCookies": false + }, + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{token}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [ + { + "key": "Authorization", + "value": "Bearer {{token}}" + }, + { + "key": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "method", + "value": "linearize" + }, + { + "key": "params", + "value": "{}" + }, + { + "key": "delivery", + "value": "" + }, + { + "key": "file", + "value": "", + "type": "file" + } + ] + }, + "url": { + "raw": "{{baseUrl}}/transformations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "transformations" + ] + }, + "description": "Deprecated / no longer supported. Included for reference." + }, + "response": [] + } + ], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Work in Progress folder: Validate status codes and response structure", + "pm.test(\"Status code is 200 or 201\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200, 201]);", + "});", + "pm.test(\"Response is an object\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.be.an('object');", + "});" + ] + } + } + ] + } + ], + "event": [ + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "// Collection-level fallback tests for Nitro Automation Platform APIs", + "pm.test(\"Status code is 2xx\", function () {", + " pm.expect(pm.response.code).to.be.within(200, 299);", + "});", + "pm.test(\"Response is JSON or empty\", function () {", + " if(pm.response.text()) {", + " pm.expect(() => pm.response.json()).not.to.throw();", + " }", + "});" + ] + } + } + ] +} \ No newline at end of file diff --git a/postman/README.md b/postman/README.md new file mode 100644 index 0000000..41c6806 --- /dev/null +++ b/postman/README.md @@ -0,0 +1,80 @@ +# Postman Collection + +This folder contains the Postman collection for the Nitro Platform API with examples for all major endpoints. + +## Getting Started + +### 1. Import the Collection + +1. Open Postman +2. Click **Import** in the top left +3. Select `Platform-API.postman_collection.json` +4. The collection will appear in your workspace + +### 2. Configure Environment Variables + +Create a new environment in Postman with these variables: + +| Postman Variable | Python .env Variable | Description | Example Value | +|------------------|---------------------|-------------|---------------| +| `baseUrl` | `PLATFORM_BASE_URL` | API base URL | `https://api.gonitro.dev` | +| `clientID` | `PLATFORM_CLIENT_ID` | Your client ID | `your-client-id` | +| `clientSecret` | `PLATFORM_CLIENT_SECRET` | Your client secret | `your-client-secret` | +| `token` | (auto-populated) | Bearer token | `eyJ0eXAiOiJKV1Q...` | + +**Note**: If you're also using the Python samples, you can use the same credential values from your `.env` file - just use the Postman variable names shown above. + +### 3. Get Authentication Token + +1. Navigate to **Authorization** → **Get Bearer Token** +2. Ensure your `clientID` and `clientSecret` are set +3. Send the POST request +4. The `token` variable will be automatically set for subsequent requests + +### 4. Run Sample Requests + +The collection is organized into folders: + +- **Authorization**: Get OAuth2 bearer tokens +- **Conversions**: Convert documents between formats +- **Extractions**: Extract text, form data, tables, and PII +- **Transformations**: Modify PDFs (compress, merge, split, etc.) +- **Jobs**: Check status and get results for async operations + +## Example Workflow + +1. **Get Token**: Run "Get Bearer Token" request +2. **Convert Document**: Use "Word → PDF" with a sample .docx file +3. **Extract Data**: Use "Extract PDF Text" on the converted PDF +4. **Transform**: Use "Compress" to reduce file size + +## File Upload + +Many endpoints require file uploads. In Postman: +1. Select **Body** → **form-data** +2. Set key to `file` (or as specified in the request) +3. Change type to **File** +4. Select your document + +**Sample Files**: The repository includes sample files in the `test_files/` folder at the root level that you can use for testing: +- `Analysis.docx` - Word document +- `Feedback.xlsx` - Excel spreadsheet +- `SamplePPTX.pptx` - PowerPoint presentation +- `SampleResume.pdf` - PDF with text and PII +- `Sample Tables.pdf` - PDF with tables +- `BOB - Student-Loan-Application-Form.pdf` - PDF with form fields + +## Async Operations + +Some operations return a job ID for async processing: +1. Submit the request and note the `jobId` in the response +2. Use **Jobs** → **Get Async Job Status** to check progress +3. When status is `completed`, use **Get Async Job Result** to download the result + +## Getting Your Credentials + +To get your API credentials: +1. Go to [https://admin.gonitro.com](https://admin.gonitro.com) +2. Navigate to **Settings** → **API** +3. Click **Create Application** +4. Name your application and save the Client ID and Client Secret diff --git a/postman/google-doc-url.json b/postman/google-doc-url.json new file mode 100644 index 0000000..70545da --- /dev/null +++ b/postman/google-doc-url.json @@ -0,0 +1 @@ +{"URL":"https://docs.google.com/document/d/0B9kwDTQ0UFjQVWtCTVBrV0ZCS3BFUXkyaHEtNFhzSzF3WEI0/edit?usp=sharing&ouid=102711825957905167280&resourcekey=0-lJH9Rilj-XkFkytoyvmwFg&rtpof=true&sd=true"} \ No newline at end of file diff --git a/power-automate/README.md b/power-automate/README.md new file mode 100644 index 0000000..1e66005 --- /dev/null +++ b/power-automate/README.md @@ -0,0 +1,89 @@ +# Power Automate Integration + +This folder contains instructions for creating a custom connector to integrate the Nitro Platform API with Microsoft Power Automate. + +## Creating Your Custom Connector + +### 1. Create New Custom Connector + +1. Go to [Power Automate](https://make.powerautomate.com) +2. Navigate to **Data** → **Custom connectors** +3. Click **New custom connector** → **Create from blank** +4. Name it "Nitro Platform API" + +### 2. Configure General Settings + +- **Description**: Document processing API for conversions, extractions, and transformations +- **Host**: `api.gonitro.dev` +- **Base URL**: `/` + +### 3. Configure Security + +1. Set **Authentication type** to **API Key** +2. **Parameter label**: API Key +3. **Parameter name**: `X-API-Key` +4. **Parameter location**: Header + +### 4. Add Key Operations + +Add these essential operations: + +#### Convert Document +- **Operation ID**: `convert-document` +- **Summary**: Convert Document +- **Verb**: POST +- **URL**: `/platform/conversions` +- **Request**: + - Body type: `multipart/form-data` + - Parameter: `file` (file upload) + - Parameter: `params` (string, JSON parameters) + +#### Extract Text +- **Operation ID**: `extract-text` +- **Summary**: Extract PDF Text +- **Verb**: POST +- **URL**: `/platform/extractions/pdf-text` +- **Request**: Body with `file` parameter + +#### Get Job Status +- **Operation ID**: `get-job-status` +- **Summary**: Get Job Status +- **Verb**: GET +- **URL**: `/jobs/{jobId}/status` +- **Parameter**: `jobId` (path parameter) + +### 5. Test Your Connector + +1. Go to **Test** tab +2. Create a new connection with your API key +3. Test the "Get Job Status" operation with a sample job ID + +## Sample Flows + +### Document Conversion Flow + +**Trigger**: When a file is added to SharePoint +1. **Get file content** from SharePoint +2. **Convert Document** using your custom connector +3. **Create file** in SharePoint with converted result + +### Batch Processing Flow + +**Trigger**: Scheduled (daily) +1. **List files** from SharePoint folder +2. **Apply to each** file: + - **Convert Document** + - **Get Job Status** (with delay/retry logic) + - **Get Job Result** when complete + - **Update file** in destination folder + +## Best Practices + +- **Error Handling**: Add try-catch blocks around API calls +- **Async Jobs**: Use "Do until" loops to poll job status +- **Rate Limits**: Add 1-2 second delays between API calls +- **File Size**: Check file size limits before processing + +## Authentication + +Get your API key from the Nitro platform dashboard and use it when creating connections to your custom connector. diff --git a/samples/python/.env.example b/samples/python/.env.example new file mode 100644 index 0000000..3a3d066 --- /dev/null +++ b/samples/python/.env.example @@ -0,0 +1,3 @@ +PLATFORM_BASE_URL=https://api.gonitro.dev +PLATFORM_CLIENT_ID=your-client-id-here +PLATFORM_CLIENT_SECRET=your-client-secret-here diff --git a/samples/python/.gitignore b/samples/python/.gitignore new file mode 100644 index 0000000..4ad6dc2 --- /dev/null +++ b/samples/python/.gitignore @@ -0,0 +1,13 @@ +# Environment +.env + +# Temporary test files and outputs +temp/ +output/ + +# Python +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python diff --git a/samples/python/README.md b/samples/python/README.md new file mode 100644 index 0000000..688fbb5 --- /dev/null +++ b/samples/python/README.md @@ -0,0 +1,109 @@ +# Python Examples + +Python examples for integrating with the Nitro Platform API. + +## Setup + +1. Install dependencies: + ```bash + pip install -r requirements.txt + ``` + +2. Copy and configure environment variables: + ```bash + cp .env.example .env + # Edit .env with your credentials + ``` + +3. Run the quickstart example: + ```bash + python quickstart.py + ``` + +## Files + +- `platform_api.py` - Main API client library +- `quickstart.py` - Authentication test +- `convert_cli.py` - Document conversion +- `extract_data.py` - Extract forms and tables +- `smart_redact_pii.py` - Auto-detect and redact PII +- `redact_by_keyword.py` - Redact specific keywords +- `batch_process.py` - Batch convert documents +- `bulk_password_protect.py` - Password protect multiple PDFs + +## Usage Examples + +### Authentication +```bash +python quickstart.py +``` + +### Convert Documents +```bash +# Convert DOCX to PDF +python convert_cli.py input.docx output.pdf pdf + +# Convert PDF to DOCX +python convert_cli.py input.pdf output.docx docx +``` + +### Extract Data +```bash +# Extract tables +python extract_data.py tables input.pdf output.json + +# Extract forms +python extract_data.py forms input.pdf output.json +``` + +### Redact Content +```bash +# Auto-detect and redact PII +python smart_redact_pii.py input.pdf output.pdf + +# Redact specific keywords +python redact_by_keyword.py input.pdf output.pdf "confidential" "secret" +``` + +### Batch Operations +```bash +# Convert all DOCX files to PDF +python batch_process.py ./input ./output pdf "*.docx" + +# Password protect all PDFs +python bulk_password_protect.py ./input ./output "MyPassword123" +``` + +## Using the API Client + +```python +from pathlib import Path +from platform_api import PlatformAPIClient + +client = PlatformAPIClient() + +# Convert document +converted = client.convert(Path("input.docx"), "pdf") +Path("output.pdf").write_bytes(converted) + +# Extract text +text_data = client.extract_text(Path("document.pdf")) + +# Detect PII +pii_data = client.detect_pii(Path("document.pdf")) + +# Redact content +redactions = [{"pageIndex": 0, "boundingBox": {...}}] +redacted = client.redact(Path("document.pdf"), redactions) +``` + +## Sample Files + +Sample files for testing are available in the `test_files/` folder at the repository root. + +## Getting Your Credentials + +1. Go to [https://admin.gonitro.com](https://admin.gonitro.com) +2. Navigate to **Settings** → **API** +3. Click **Create Application** +4. Save the Client ID and Client Secret to your `.env` file diff --git a/samples/python/Taskfile.yml b/samples/python/Taskfile.yml new file mode 100644 index 0000000..3e3fc49 --- /dev/null +++ b/samples/python/Taskfile.yml @@ -0,0 +1,55 @@ +version: '3' + +tasks: + smart-redact: + desc: "Smart redact PII from a document (e.g., task smart-redact INPUT=resume.pdf OUTPUT=redacted.pdf)" + cmds: + - python smart_redact_pii.py {{.INPUT}} {{.OUTPUT}} + requires: + vars: [INPUT, OUTPUT] + + convert: + desc: "Convert document from CLI (e.g., task convert INPUT=doc.docx OUTPUT=doc.pdf FORMAT=pdf)" + cmds: + - python convert_cli.py {{.INPUT}} {{.OUTPUT}} {{.FORMAT}} + requires: + vars: [INPUT, OUTPUT, FORMAT] + + batch: + desc: "Batch process documents (e.g., task batch INPUT_DIR=./docs OUTPUT_DIR=./output FORMAT=pdf PATTERN='*.docx')" + cmds: + - python batch_process.py "{{.INPUT_DIR}}" "{{.OUTPUT_DIR}}" {{.FORMAT}} "{{.PATTERN | default "*"}}" + requires: + vars: [INPUT_DIR, OUTPUT_DIR, FORMAT] + + extract: + desc: "Extract forms or tables from PDF (e.g., task extract MODE=forms INPUT=form.pdf OUTPUT=data.json)" + cmds: + - python extract_data.py {{.MODE}} "{{.INPUT}}" "{{.OUTPUT}}" + requires: + vars: [MODE, INPUT, OUTPUT] + + password-protect: + desc: "Bulk password protect PDFs (e.g., task password-protect INPUT_DIR=./pdfs OUTPUT_DIR=./protected PASSWORD=secret123)" + cmds: + - python bulk_password_protect.py "{{.INPUT_DIR}}" "{{.OUTPUT_DIR}}" "{{.PASSWORD}}" + requires: + vars: [INPUT_DIR, OUTPUT_DIR, PASSWORD] + + redact-keyword: + desc: "Redact by keyword search (e.g., task redact-keyword INPUT=doc.pdf OUTPUT=redacted.pdf KEYWORDS='confidential secret')" + cmds: + - python redact_by_keyword.py {{.INPUT}} {{.OUTPUT}} {{.KEYWORDS}} + requires: + vars: [INPUT, OUTPUT, KEYWORDS] + + install: + desc: "Install Python dependencies" + cmds: + - pip install -r requirements.txt + + setup: + desc: "Setup environment (copy .env.example to .env)" + cmds: + - cp .env.example .env + - echo "✅ Created .env file - please update with your credentials" diff --git a/samples/python/batch_process.py b/samples/python/batch_process.py new file mode 100644 index 0000000..52ec9dd --- /dev/null +++ b/samples/python/batch_process.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python +"""Batch process documents from CLI.""" + +import sys +from pathlib import Path +from platform_api import PlatformAPIClient + +if __name__ == "__main__": + if len(sys.argv) < 4: + print("Usage: python batch_process.py [pattern]") + print("Example: python batch_process.py ./docs ./output pdf '*.docx'") + sys.exit(1) + + input_dir = Path(sys.argv[1]) + output_dir = Path(sys.argv[2]) + to_format = sys.argv[3] + pattern = sys.argv[4] if len(sys.argv) > 4 else "*" + + output_dir.mkdir(exist_ok=True) + files = list(input_dir.glob(pattern)) + + if not files: + print(f"❌ No files matching '{pattern}' found in {input_dir}") + sys.exit(1) + + client = PlatformAPIClient() + print(f"📁 Processing {len(files)} files...") + + for i, file_path in enumerate(files, 1): + try: + print(f"[{i}/{len(files)}] Converting {file_path.name}...") + converted = client.convert(file_path, to_format) + output_path = output_dir / f"{file_path.stem}.{to_format}" + output_path.write_bytes(converted) + print(f" ✅ Saved to {output_path.name}") + except Exception as e: + print(f" ❌ Error: {e}") + + print(f"✅ Batch processing complete") diff --git a/samples/python/bulk_password_protect.py b/samples/python/bulk_password_protect.py new file mode 100644 index 0000000..55e34f0 --- /dev/null +++ b/samples/python/bulk_password_protect.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +"""Password protect PDFs in bulk.""" + +import sys +from pathlib import Path +from platform_api import PlatformAPIClient + +if __name__ == "__main__": + if len(sys.argv) != 4: + print("Usage: python bulk_password_protect.py ") + sys.exit(1) + + input_dir = Path(sys.argv[1]) + output_dir = Path(sys.argv[2]) + password = sys.argv[3] + + output_dir.mkdir(exist_ok=True) + files = list(input_dir.glob("*.pdf")) + + if not files: + print(f"❌ No PDF files found in {input_dir}") + sys.exit(1) + + client = PlatformAPIClient() + print(f"🔒 Protecting {len(files)} PDFs with password...") + + for i, file_path in enumerate(files, 1): + try: + print(f"[{i}/{len(files)}] Protecting {file_path.name}...") + protected = client.password_protect(file_path, password) + output_path = output_dir / file_path.name + output_path.write_bytes(protected) + print(f" ✅ Saved to {output_path.name}") + except Exception as e: + print(f" ❌ Error: {e}") + + print(f"✅ Bulk password protection complete") diff --git a/samples/python/convert_cli.py b/samples/python/convert_cli.py new file mode 100644 index 0000000..7cd8a9b --- /dev/null +++ b/samples/python/convert_cli.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Document conversion from CLI.""" + +import sys +from pathlib import Path +from platform_api import PlatformAPIClient + +if __name__ == "__main__": + if len(sys.argv) != 4: + print("Usage: python convert_cli.py ") + print("Formats: pdf, docx, xlsx, png, jpg") + sys.exit(1) + + input_path = Path(sys.argv[1]) + output_path = Path(sys.argv[2]) + to_format = sys.argv[3] + + client = PlatformAPIClient() + print(f"🔄 Converting {input_path.name} to {to_format}...") + converted = client.convert(input_path, to_format) + output_path.write_bytes(converted) + print(f"✅ Saved to {output_path}") diff --git a/samples/python/extract_data.py b/samples/python/extract_data.py new file mode 100644 index 0000000..433a1f5 --- /dev/null +++ b/samples/python/extract_data.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +"""Extract forms and tables from documents.""" + +import sys +import json +from pathlib import Path +from platform_api import PlatformAPIClient + +if __name__ == "__main__": + if len(sys.argv) != 4: + print("Usage: python extract_data.py ") + sys.exit(1) + + mode = sys.argv[1] + input_path = Path(sys.argv[2]) + output_path = Path(sys.argv[3]) + + client = PlatformAPIClient() + + if mode == "forms": + print(f"📋 Extracting form data from {input_path.name}...") + data = client.extract_forms(input_path) + elif mode == "tables": + print(f"📊 Extracting table data from {input_path.name}...") + data = client.extract_tables(input_path) + else: + print("❌ Mode must be 'forms' or 'tables'") + sys.exit(1) + + output_path.write_text(json.dumps(data, indent=2)) + print(f"✅ Saved to {output_path}") diff --git a/samples/python/platform_api.py b/samples/python/platform_api.py new file mode 100644 index 0000000..3deed2d --- /dev/null +++ b/samples/python/platform_api.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python +"""Platform API client for Nitro Platform integrations.""" + +import os +import time +import json +import mimetypes +import requests +from dataclasses import dataclass, field +from pathlib import Path +from typing import Any, Literal +from dotenv import load_dotenv + +load_dotenv() + + +@dataclass +class PlatformAPIClient: + """Synchronous client for Nitro Platform API operations.""" + + base_url: str = field(default_factory=lambda: os.getenv('PLATFORM_BASE_URL', 'https://api.gonitro.dev')) + client_id: str = field(default_factory=lambda: os.getenv('PLATFORM_CLIENT_ID')) + client_secret: str = field(default_factory=lambda: os.getenv('PLATFORM_CLIENT_SECRET')) + _token: str | None = field(default=None, init=False) + _token_expiry: float = field(default=0, init=False) + + def _get_token(self) -> str: + """Get or refresh OAuth2 access token.""" + if self._token and time.time() < self._token_expiry: + return self._token + + response = requests.post( + f"{self.base_url}/oauth/token", + json={"clientID": self.client_id, "clientSecret": self.client_secret} + ) + response.raise_for_status() + data = response.json() + self._token = data['accessToken'] + self._token_expiry = time.time() + data.get('expiresIn', 3600) - 60 + return self._token + + def _request( + self, + endpoint: Literal["conversions", "extractions", "transformations"], + method: str, + file_path: Path, + params: dict[str, Any] = None + ) -> dict[str, Any]: + """Make API request with file upload.""" + headers = {"Authorization": f"Bearer {self._get_token()}"} + + # Detect MIME type or use octet-stream as fallback + mime_type = mimetypes.guess_type(file_path)[0] or 'application/octet-stream' + + # Read file into memory and include content-type + files = {'file': (file_path.name, file_path.read_bytes(), mime_type)} + data = { + 'method': method, + 'params': json.dumps(params or {}) + } + + response = requests.post( + f"{self.base_url}/{endpoint}", + headers=headers, + files=files, + data=data + ) + + response.raise_for_status() + return response.json() + + def _request_bytes( + self, + endpoint: Literal["conversions", "extractions", "transformations"], + method: str, + file_path: Path, + params: dict[str, Any] = None + ) -> bytes: + """Make API request and return raw bytes.""" + headers = {"Authorization": f"Bearer {self._get_token()}"} + + # Detect MIME type or use octet-stream as fallback + mime_type = mimetypes.guess_type(file_path)[0] or 'application/octet-stream' + + # Read file into memory and include content-type + files = {'file': (file_path.name, file_path.read_bytes(), mime_type)} + data = { + 'method': method, + 'params': json.dumps(params or {}) + } + + response = requests.post( + f"{self.base_url}/{endpoint}", + headers=headers, + files=files, + data=data + ) + + response.raise_for_status() + result = response.json() + + # Download from S3 URL + download_url = result['result']['file']['URL'] + download_response = requests.get(download_url) + download_response.raise_for_status() + return download_response.content + + def convert(self, file_path: Path, to_format: str) -> bytes: + """Convert document to specified format.""" + return self._request_bytes("conversions", "convert", file_path, {"to": to_format}) + + def extract_text(self, file_path: Path) -> dict[str, Any]: + """Extract text from document.""" + return self._request("extractions", "extract-text", file_path) + + def extract_forms(self, file_path: Path) -> dict[str, Any]: + """Extract form data from PDF.""" + return self._request("extractions", "extract-forms", file_path) + + def extract_tables(self, file_path: Path) -> dict[str, Any]: + """Extract table data from PDF.""" + return self._request("extractions", "extract-tables", file_path) + + def detect_pii(self, file_path: Path, language: str = "en") -> dict[str, Any]: + """Detect PII and return bounding boxes.""" + return self._request("extractions", "pii-bbox-extraction", file_path, {"language": language}) + + def find_text_boxes(self, file_path: Path, texts: list[str]) -> dict[str, Any]: + """Find bounding boxes for specified text strings.""" + return self._request("extractions", "text-bbox-extraction", file_path, {"texts": texts}) + + def redact(self, file_path: Path, redactions: list[dict]) -> bytes: + """Redact specified bounding boxes.""" + return self._request_bytes("transformations", "redact", file_path, {"redactions": redactions}) + + def password_protect(self, file_path: Path, password: str) -> bytes: + """Add password protection to PDF.""" + return self._request_bytes( + "transformations", + "protect", + file_path, + {"ownerPassword": password, "userPassword": password} + ) + + def compress(self, file_path: Path, level: int = 2) -> bytes: + """Compress PDF (level 1-3).""" + return self._request_bytes("transformations", "compress", file_path, {"level": level}) + + def merge(self, file_paths: list[Path]) -> bytes: + """Merge multiple PDFs.""" + headers = {"Authorization": f"Bearer {self._get_token()}"} + + files = [('file', open(fp, 'rb')) for fp in file_paths] + data = {'method': 'merge', 'params': '{}'} + + try: + response = requests.post( + f"{self.base_url}/transformations", + headers=headers, + files=files, + data=data + ) + response.raise_for_status() + return response.content + finally: + for _, f in files: + f.close() diff --git a/samples/python/quickstart.py b/samples/python/quickstart.py new file mode 100644 index 0000000..300d9b2 --- /dev/null +++ b/samples/python/quickstart.py @@ -0,0 +1,71 @@ +import os +import requests +from dotenv import load_dotenv + +load_dotenv() + +BASE_URL = os.getenv('PLATFORM_BASE_URL', 'https://api.gonitro.dev') +CLIENT_ID = os.getenv('PLATFORM_CLIENT_ID') +CLIENT_SECRET = os.getenv('PLATFORM_CLIENT_SECRET') + +def get_access_token(): + """Get OAuth2 access token using client credentials""" + url = f"{BASE_URL}/oauth/token" + data = { + "clientID": CLIENT_ID, + "clientSecret": CLIENT_SECRET + } + + response = requests.post(url, json=data) + response.raise_for_status() + return response.json()['accessToken'] + +def test_connection(token): + """Test API connection with a simple request""" + url = f"{BASE_URL}/jobs/test-job-id/status" + headers = {"Authorization": f"Bearer {token}"} + + try: + response = requests.get(url, headers=headers) + # 404 is expected for non-existent job, but proves auth works + if response.status_code == 404: + print("✅ Authentication successful (404 expected for test job ID)") + return True + response.raise_for_status() + return True + except requests.exceptions.HTTPError as e: + if e.response.status_code == 404: + print("✅ Authentication successful (404 expected for test job ID)") + return True + raise + +def main(): + if not CLIENT_ID or not CLIENT_SECRET: + print("❌ Missing credentials!") + print("To get your credentials:") + print("1. Go to https://admin.gonitro.com") + print("2. Navigate to Settings → API") + print("3. Click 'Create Application'") + print("4. Name your application and save the Client ID and Client Secret") + print("5. Set environment variables:") + print(" export PLATFORM_CLIENT_ID=") + print(" export PLATFORM_CLIENT_SECRET=") + return + + try: + print("🔐 Getting access token...") + token = get_access_token() + print("✅ Token obtained successfully") + + print("🧪 Testing API connection...") + test_connection(token) + print("✅ API connection successful") + + print("\n🎉 Setup complete! You can now use the Platform API.") + print("📖 See https://developers.gonitro.com/docs for API documentation") + + except Exception as e: + print(f"❌ Error: {e}") + +if __name__ == "__main__": + main() diff --git a/samples/python/redact_by_keyword.py b/samples/python/redact_by_keyword.py new file mode 100644 index 0000000..a84b81c --- /dev/null +++ b/samples/python/redact_by_keyword.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +"""Redact by keyword search.""" + +import sys +from pathlib import Path +from platform_api import PlatformAPIClient + +if __name__ == "__main__": + if len(sys.argv) < 4: + print("Usage: python redact_by_keyword.py [keyword2 ...]") + sys.exit(1) + + input_path = Path(sys.argv[1]) + output_path = Path(sys.argv[2]) + keywords = sys.argv[3:] + + client = PlatformAPIClient() + + print(f"🔍 Finding keywords in {input_path.name}...") + bbox_data = client.find_text_boxes(input_path, keywords) + + text_boxes = bbox_data.get('result', {}).get('textBoxes', []) + if not text_boxes: + print("✅ No keywords found") + sys.exit(0) + + print(f"🎯 Found {len(text_boxes)} keyword instances") + print(f"🔒 Redacting keywords...") + + redactions = [{"pageIndex": box["pageIndex"], "boundingBox": box["boundingBox"]} + for box in text_boxes] + + redacted = client.redact(input_path, redactions) + output_path.write_bytes(redacted) + print(f"✅ Saved to {output_path}") diff --git a/samples/python/requirements.txt b/samples/python/requirements.txt new file mode 100644 index 0000000..2b1be2a --- /dev/null +++ b/samples/python/requirements.txt @@ -0,0 +1,2 @@ +requests>=2.31.0 +python-dotenv>=1.0.0 diff --git a/samples/python/smart_redact_pii.py b/samples/python/smart_redact_pii.py new file mode 100644 index 0000000..75b45da --- /dev/null +++ b/samples/python/smart_redact_pii.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +"""Smart redact PII from documents.""" + +import sys +from pathlib import Path +from platform_api import PlatformAPIClient + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: python smart_redact_pii.py ") + sys.exit(1) + + input_path = Path(sys.argv[1]) + output_path = Path(sys.argv[2]) + + client = PlatformAPIClient() + + print(f"🔍 Detecting PII in {input_path.name}...") + pii_data = client.detect_pii(input_path) + + pii_boxes = pii_data.get('result', {}).get('PIIBoxes', []) + if not pii_boxes: + print("✅ No PII detected") + sys.exit(0) + + print(f"🎯 Found {len(pii_boxes)} PII instances") + print(f"🔒 Redacting PII...") + + redactions = [{"pageIndex": box["pageIndex"], "boundingBox": box["boundingBox"]} + for box in pii_boxes] + + redacted = client.redact(input_path, redactions) + output_path.write_bytes(redacted) + print(f"✅ Saved to {output_path}") diff --git a/test_files/SamplePPTX.pptx b/test_files/SamplePPTX.pptx new file mode 100755 index 0000000..af58bd1 Binary files /dev/null and b/test_files/SamplePPTX.pptx differ diff --git a/test_files/test-batch/Analysis.docx b/test_files/test-batch/Analysis.docx new file mode 100755 index 0000000..db8c2ac Binary files /dev/null and b/test_files/test-batch/Analysis.docx differ diff --git a/test_files/test-batch/Feedback.xlsx b/test_files/test-batch/Feedback.xlsx new file mode 100755 index 0000000..dabc4d5 Binary files /dev/null and b/test_files/test-batch/Feedback.xlsx differ diff --git a/test_files/test-pdfs/BOB - Student-Loan-Application-Form.pdf b/test_files/test-pdfs/BOB - Student-Loan-Application-Form.pdf new file mode 100755 index 0000000..46053a8 Binary files /dev/null and b/test_files/test-pdfs/BOB - Student-Loan-Application-Form.pdf differ diff --git a/test_files/test-pdfs/Sample Tables.pdf b/test_files/test-pdfs/Sample Tables.pdf new file mode 100755 index 0000000..e1d49cd Binary files /dev/null and b/test_files/test-pdfs/Sample Tables.pdf differ diff --git a/test_files/test-pdfs/SampleResume.pdf b/test_files/test-pdfs/SampleResume.pdf new file mode 100755 index 0000000..c99d3cc Binary files /dev/null and b/test_files/test-pdfs/SampleResume.pdf differ