Back to blog
April 9, 202612 min read

Automate Passport Data Extraction with Passport OCR and n8n

n8nautomationno-codeintegrationworkflow

n8n is one of the most popular open-source workflow automation tools — think Zapier, but self-hostable and far more flexible. If you're already using n8n to automate your business processes, adding passport data extraction is just one HTTP Request node away.

In this guide, we'll show you how to connect Passport OCR to n8n and build real automation workflows — no code required.

Why Passport OCR + n8n?

n8n is great at connecting services together. Passport OCR is great at reading passports. Together, they let you build automations like:

  • Email-to-data pipeline — a guest emails their passport photo, n8n extracts the data and adds it to a spreadsheet
  • Form submission processing — a Typeform or Tally submission with a passport image gets auto-processed
  • Hotel PMS integration — passport scan at check-in auto-populates your property management system
  • n8n Form check-in — share a form link with guests, they upload their passport and you get structured data instantly
  • Google Sheets logging — every passport scan gets logged to a shared spreadsheet automatically
  • CRM enrichment — extract passport data and update a HubSpot or Airtable record

The best part: Passport OCR's API is a single endpoint with simple input/output, which makes it trivially easy to use in n8n's HTTP Request node.

Setting Up the HTTP Request Node

Passport OCR doesn't need a dedicated n8n integration — the built-in HTTP Request node handles everything.

Basic Configuration

  1. Add an HTTP Request node to your workflow
  2. Set the method to POST
  3. Set the URL to https://passport-ocr.com/v1/ocr
  4. Under Authentication, select Generic Credential TypeHeader Auth
    • Name: Authorization
    • Value: Bearer passport_ocr_your_key_here

Sending an Image File

If your workflow receives a file (from an email attachment, form upload, webhook, etc.):

  1. Set Body Content Type to Form-Data / Multipart
  2. Add a parameter:
    • Parameter Type: Form Data
    • Name: image
    • Input Data Field Name: select the binary field from the previous node (usually data or attachment_0)

Sending a Base64 Image

If you have the image as a base64 string:

  1. Set Body Content Type to JSON
  2. Set the JSON body to:
{
  "image": "data:image/jpeg;base64,{{ $json.imageBase64 }}"
}

Reading the Response

The response comes back as clean JSON:

{
  "success": true,
  "data": {
    "documentType": "PASSPORT",
    "documentNumber": "L898902C3",
    "issuingCountry": "UTO",
    "lastName": "ERIKSSON",
    "firstName": "ANNA MARIA",
    "nationality": "UTO",
    "dateOfBirth": "1974-08-12",
    "sex": "female",
    "expirationDate": "2012-04-15",
    "personalNumber": "ZE184226B"
  }
}

In subsequent nodes, access the fields with expressions like {{ $json.data.firstName }}, {{ $json.data.lastName }}, etc.

Example Workflow: Email Passport → Google Sheets

Here's a practical workflow that many small hotels and Airbnb hosts can use:

Trigger: A guest emails their passport photo to a dedicated address (e.g., [email protected])

Flow:

  1. Email Trigger (IMAP) — watches for new emails with attachments
  2. HTTP Request — sends the attachment to Passport OCR
  3. IF — checks if success is true
  4. Google Sheets — appends a row with the extracted data
  5. Send Email — confirms to the guest that check-in data was received

Node-by-Node Setup

Node 1: Email Trigger (IMAP)

  • Configure with your email credentials
  • Enable Download Attachments

Node 2: HTTP Request (Passport OCR)

  • Method: POST
  • URL: https://passport-ocr.com/v1/ocr
  • Authentication: Header Auth with your API key
  • Body: Form-Data, image field mapped to the email attachment binary

Node 3: IF

  • Condition: {{ $json.success }} equals true
  • True branch → continue to Google Sheets
  • False branch → send an error notification

Node 4: Google Sheets (Append Row)

  • Map columns:
    • Guest Name: {{ $json.data.firstName }} {{ $json.data.lastName }}
    • Nationality: {{ $json.data.nationality }}
    • Date of Birth: {{ $json.data.dateOfBirth }}
    • Passport Number: {{ $json.data.documentNumber }}
    • Expiry Date: {{ $json.data.expirationDate }}
    • Check-in Date: {{ $now.format('yyyy-MM-dd') }}

Node 5: Send Email (Reply)

  • To: {{ $('Email Trigger').item.json.from }}
  • Subject: "Check-in confirmed"
  • Body: "Thanks! We've received your passport details. See you soon."

That's the entire workflow — five nodes, zero code.

Copy this template

Copy the JSON below and import it into n8n (Workflows → Import from JSON). You'll just need to add your email credentials, Google Sheets credentials, and Passport OCR API key.

{
  "name": "Email Passport → Google Sheets",
  "nodes": [
    {
      "parameters": {
        "pollTimes": { "item": [{ "mode": "everyMinute" }] },
        "simple": false,
        "downloadAttachments": true,
        "options": {}
      },
      "type": "n8n-nodes-base.emailReadImap",
      "typeVersion": 2,
      "position": [240, 300],
      "id": "email-trigger",
      "name": "Email Trigger (IMAP)",
      "credentials": { "imap": { "id": "YOUR_IMAP_CREDENTIAL_ID", "name": "Your IMAP Account" } }
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://passport-ocr.com/v1/ocr",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "image",
              "inputDataFieldName": "attachment_0"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [480, 300],
      "id": "passport-ocr",
      "name": "Passport OCR",
      "credentials": { "httpHeaderAuth": { "id": "YOUR_HEADER_AUTH_ID", "name": "Passport OCR API Key" } }
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true, "leftValue": "" },
          "conditions": [
            {
              "leftValue": "={{ $json.success }}",
              "rightValue": true,
              "operator": { "type": "boolean", "operation": "equals" }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [700, 300],
      "id": "check-success",
      "name": "OCR Success?"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": { "__rl": true, "mode": "list", "value": "YOUR_SPREADSHEET_ID" },
        "sheetName": { "__rl": true, "mode": "list", "value": "Sheet1" },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Guest Name": "={{ $json.data.firstName }} {{ $json.data.lastName }}",
            "Nationality": "={{ $json.data.nationality }}",
            "Date of Birth": "={{ $json.data.dateOfBirth }}",
            "Passport Number": "={{ $json.data.documentNumber }}",
            "Expiry Date": "={{ $json.data.expirationDate }}",
            "Check-in Date": "={{ $now.format('yyyy-MM-dd') }}"
          }
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [940, 200],
      "id": "google-sheets",
      "name": "Log to Google Sheets",
      "credentials": { "googleSheetsOAuth2Api": { "id": "YOUR_GOOGLE_CREDENTIAL_ID", "name": "Google Sheets" } }
    },
    {
      "parameters": {
        "sendTo": "={{ $('Email Trigger (IMAP)').item.json.from }}",
        "subject": "Check-in confirmed",
        "message": "Thanks! We've received your passport details. See you soon.",
        "options": {}
      },
      "type": "n8n-nodes-base.emailSend",
      "typeVersion": 2.1,
      "position": [1160, 200],
      "id": "send-confirmation",
      "name": "Confirm to Guest",
      "credentials": { "smtp": { "id": "YOUR_SMTP_CREDENTIAL_ID", "name": "Your SMTP Account" } }
    }
  ],
  "connections": {
    "Email Trigger (IMAP)": { "main": [[{ "node": "Passport OCR", "type": "main", "index": 0 }]] },
    "Passport OCR": { "main": [[{ "node": "OCR Success?", "type": "main", "index": 0 }]] },
    "OCR Success?": { "main": [[{ "node": "Log to Google Sheets", "type": "main", "index": 0 }], []] },
    "Log to Google Sheets": { "main": [[{ "node": "Confirm to Guest", "type": "main", "index": 0 }]] }
  },
  "pinData": {},
  "settings": { "executionOrder": "v1" },
  "tags": []
}

Example Workflow: n8n Form → Passport OCR

This is the easiest way to get started — no external tools needed. n8n has a built-in Form Trigger that creates a hosted form with a shareable link. Guests open the link, upload their passport photo, and the workflow extracts the data automatically.

Perfect for:

  • Airbnb hosts — send the form link in your check-in instructions
  • Small hotels — use it as a digital registration card
  • Tour operators — collect passport details before the trip
  • Any SME — instant passport data collection without building an app

Flow:

  1. n8n Form Trigger — hosted form with a file upload field
  2. HTTP Request — sends the uploaded image to Passport OCR
  3. n8n Form Completion — shows the extracted data back to the guest for confirmation

Node-by-Node Setup

Node 1: n8n Form Trigger

  • Form Title: "Guest Check-In"
  • Form Description: "Please upload a photo of your passport data page"
  • Fields:
    • Guest Name (text, required) — so you can greet them
    • Passport Photo (file, required) — accepts image files

Node 2: HTTP Request (Passport OCR)

  • Method: POST
  • URL: https://passport-ocr.com/v1/ocr
  • Authentication: Header Auth with your API key
  • Body: Form-Data, image field mapped to the uploaded file binary

Node 3: n8n Form Completion

  • Title: "Check-In Complete"
  • Message: Display the extracted data so the guest can confirm it's correct

Share the form URL with your guests — that's your entire check-in system.

Copy this template

{
  "name": "Guest Check-In Form → Passport OCR",
  "nodes": [
    {
      "parameters": {
        "formTitle": "Guest Check-In",
        "formDescription": "Please upload a photo of your passport data page. The image should clearly show the two lines of text at the bottom of the page.",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Full Name",
              "fieldType": "text",
              "requiredField": true,
              "placeholder": "e.g. Anna Eriksson"
            },
            {
              "fieldLabel": "Passport Photo",
              "fieldType": "file",
              "requiredField": true,
              "acceptFileTypes": ".jpg,.jpeg,.png,.webp"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [240, 300],
      "id": "form-trigger",
      "name": "Guest Check-In Form",
      "webhookId": "passport-checkin"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://passport-ocr.com/v1/ocr",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "parameterType": "formBinaryData",
              "name": "image",
              "inputDataFieldName": "Passport Photo"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [480, 300],
      "id": "passport-ocr-form",
      "name": "Passport OCR",
      "credentials": { "httpHeaderAuth": { "id": "YOUR_HEADER_AUTH_ID", "name": "Passport OCR API Key" } }
    },
    {
      "parameters": {
        "conditions": {
          "options": { "caseSensitive": true, "leftValue": "" },
          "conditions": [
            {
              "leftValue": "={{ $json.success }}",
              "rightValue": true,
              "operator": { "type": "boolean", "operation": "equals" }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [700, 300],
      "id": "check-success-form",
      "name": "OCR Success?"
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "=## ✅ Check-In Complete\n\nThank you, {{ $('Guest Check-In Form').item.json['Full Name'] }}! Here are your passport details:\n\n- **Name:** {{ $json.data.firstName }} {{ $json.data.lastName }}\n- **Nationality:** {{ $json.data.nationality }}\n- **Date of Birth:** {{ $json.data.dateOfBirth }}\n- **Passport Number:** {{ $json.data.documentNumber }}\n- **Expiry Date:** {{ $json.data.expirationDate }}\n\nIf anything looks incorrect, please contact us.",
        "options": {}
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [940, 200],
      "id": "form-completion",
      "name": "Show Results"
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "## ❌ Could Not Read Passport\n\nSorry, we couldn't extract data from your passport photo. Please try again with a clearer image — make sure the bottom of the passport page (the two lines of small text) is visible and in focus.",
        "options": {}
      },
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2.2,
      "position": [940, 420],
      "id": "form-error",
      "name": "Show Error"
    }
  ],
  "connections": {
    "Guest Check-In Form": { "main": [[{ "node": "Passport OCR", "type": "main", "index": 0 }]] },
    "Passport OCR": { "main": [[{ "node": "OCR Success?", "type": "main", "index": 0 }]] },
    "OCR Success?": {
      "main": [
        [{ "node": "Show Results", "type": "main", "index": 0 }],
        [{ "node": "Show Error", "type": "main", "index": 0 }]
      ]
    }
  },
  "pinData": {},
  "settings": { "executionOrder": "v1" },
  "tags": []
}

To use this template:

  1. Copy the JSON above
  2. In n8n, go to Workflows → Import from JSON
  3. Paste and click Import
  4. Add your Passport OCR API key as a Header Auth credential (Authorization: Bearer passport_ocr_xxx)
  5. Activate the workflow — you'll get a shareable form URL

You can extend this by adding a Google Sheets node after the success branch to log all check-ins, or an Email node to send a confirmation to yourself.

Error Handling

Passport OCR returns clear error codes that are easy to handle in n8n with an IF or Switch node:

Error CodeWhat to Do in n8n
INVALID_IMAGENotify the user to upload a clearer image
MRZ_NOT_DETECTEDThe image might not contain a passport — ask for a re-upload
OCR_FAILEDMRZ was found but unreadable — request a better photo
RATE_LIMIT_EXCEEDEDFree tier limit hit — add a Wait node or upgrade to paid
INSUFFICIENT_CREDITSSend yourself an alert to top up credits

Add an error branch after the HTTP Request node to handle these gracefully instead of letting the workflow fail silently.

Tips for Production Workflows

Use credentials, not hardcoded keys

Store your API key in n8n's Credentials (Header Auth type) rather than pasting it directly into the node. This keeps it secure and makes it easy to rotate.

Add a Wait node for rate limiting

If you're on the free tier (10 requests/day), add a Wait node before the HTTP Request to space out requests. On the paid tier, this isn't necessary — there are no rate limits.

Use the Dry Run header for validation

Add X-Dry-Run: true as a header to validate that an image contains a readable MRZ without performing full OCR. This is free and doesn't consume credits — useful as a pre-check before queuing expensive downstream actions.

Log everything to a spreadsheet

Even if your main destination is a CRM or PMS, consider adding a Google Sheets node as a parallel branch to keep a searchable log of all scans. Useful for debugging and auditing.

Cost in Automation Context

At $0.01 per request, Passport OCR is negligible in most automation budgets:

Workflow VolumeMonthly Cost
10 scans/day (free tier)$0
50 scans/day$15/month
200 scans/day$60/month

Compare that to the time saved from manual data entry. If a single manual passport transcription takes 2 minutes, 200 scans/day saves over 6 hours of work — every day.

Getting Started

  1. Get your API key — sign up at passport-ocr.com/auth/login and grab your key from the dashboard
  2. Add credentials in n8n — create a Header Auth credential with your API key
  3. Build your first workflow — import one of the JSON templates above and customize it
  4. Test with the free tier — 10 requests/day, no credit card needed
  5. Check the API docs for the full endpoint reference and error codes

n8n makes automation accessible. Passport OCR makes passport data extraction simple. Put them together and you've got a powerful, no-code pipeline for any workflow that touches travel documents.

Ready to extract passport data?

Try Passport OCR free — 10 requests per day, no signup required.