AI
Pass

Build a Passport Photo Maker App with AI Pass SDK

Build a Passport Photo Maker App with AI Pass SDK

In this tutorial, you'll learn to build a full-featured passport photo maker app using the AI Pass SDK. Your users can upload any photo and get compliant passport photos instantly - and they pay directly to AI Pass, so you never worry about API costs.

You earn 50% commission on every API call your users make.

What We're Building

A passport photo maker app that:

  • Accepts any photo upload
  • Lets users select their country
  • Uses AI to:
    • Remove/replace background
    • Crop to passport dimensions
    • Validate compliance
  • Downloads print-ready images

Prerequisites

  • Basic HTML/CSS/JavaScript knowledge
  • An AI Pass account (free to create)

Step 1: Create Your AI Pass Account

  1. Go to AI Pass and click Sign Up
  2. Enter your email and create a password
  3. Verify your email address
  4. You get $1 free credit automatically

Step 2: Create an OAuth Client

To use the SDK and earn commission, you need an OAuth client:

  1. Go to Developer Dashboard
  2. Click OAuth2 Clients β†’ Create New Client
  3. Enter a name (e.g., "My Passport Photo App")
  4. Set redirect URL to your domain (e.g., https://yourdomain.com/callback)
  5. Click Create
  6. Copy your Client ID - it looks like client_abc123...

⚠️ Important: Never share your Client ID publicly. It's like a password.

Step 3: Initialize the AI Pass SDK

Create an index.html file and add the SDK:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Passport Photo Maker</title>
  <link href="https://aipass.one/aipass-ui.css" rel="stylesheet">
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      max-width: 600px;
      margin: 0 auto;
      padding: 20px;
      background: #f5f5f5;
    }
    .container {
      background: white;
      padding: 30px;
      border-radius: 12px;
      box-shadow: 0 2px 10px rgba(0,0,0,0.1);
    }
    h1 {
      color: #333;
      margin-bottom: 20px;
    }
    .upload-area {
      border: 2px dashed #ccc;
      padding: 40px;
      text-align: center;
      cursor: pointer;
      border-radius: 8px;
      margin-bottom: 20px;
    }
    .upload-area:hover {
      border-color: #007bff;
      background: #f8f9fa;
    }
    select, button {
      width: 100%;
      padding: 12px;
      margin-bottom: 15px;
      border: 1px solid #ddd;
      border-radius: 6px;
      font-size: 16px;
    }
    button {
      background: #007bff;
      color: white;
      border: none;
      cursor: pointer;
    }
    button:hover {
      background: #0056b3;
    }
    button:disabled {
      background: #ccc;
      cursor: not-allowed;
    }
    .preview {
      margin-top: 20px;
      text-align: center;
    }
    .preview img {
      max-width: 100%;
      border-radius: 8px;
    }
    .error {
      color: #dc3545;
      padding: 10px;
      background: #f8d7da;
      border-radius: 6px;
      margin-bottom: 15px;
    }
    .success {
      color: #155724;
      padding: 10px;
      background: #d4edda;
      border-radius: 6px;
      margin-bottom: 15px;
    }
  </style>
</head>
<body>
  <div class="container">
    <h1>πŸ“· Passport Photo Maker</h1>
    <p>Upload any photo and get a compliant passport photo instantly.</p>
    
    <div class="upload-area" id="uploadArea">
      <p>πŸ“ Click or drag photo here</p>
      <input type="file" id="fileInput" accept="image/*" style="display: none;">
    </div>
    
    <label for="country">Select Country:</label>
    <select id="country">
      <option value="us">πŸ‡ΊπŸ‡Έ United States (2x2 inch)</option>
      <option value="uk">πŸ‡¬πŸ‡§ United Kingdom (35x45mm)</option>
      <option value="eu">πŸ‡ͺπŸ‡Ί European Union (35x45mm)</option>
      <option value="ca">πŸ‡¨πŸ‡¦ Canada (50x70mm)</option>
      <option value="au">πŸ‡¦πŸ‡Ί Australia (35x45mm)</option>
    </select>
    
    <button id="generateBtn" disabled>Generate Passport Photo</button>
    
    <div id="status"></div>
    <div class="preview" id="preview"></div>
  </div>

  <script src="https://aipass.one/aipass-sdk.js"></script>
  <script>
    // Initialize SDK - REPLACE WITH YOUR CLIENT ID
    AiPass.initialize({
      clientId: 'YOUR_CLIENT_ID',
      requireLogin: true,
      darkMode: false
    });

    let uploadedFile = null;
    const countrySpecs = {
      us: { width: 600, height: 600, background: '#ffffff', name: 'US' },
      uk: { width: 413, height: 531, background: '#f5f5dc', name: 'UK' },
      eu: { width: 413, height: 531, background: '#d3d3d3', name: 'EU' },
      ca: { width: 590, height: 827, background: '#ffffff', name: 'Canada' },
      au: { width: 413, height: 531, background: '#f5f5dc', name: 'Australia' }
    };

    // File upload handling
    const uploadArea = document.getElementById('uploadArea');
    const fileInput = document.getElementById('fileInput');
    const generateBtn = document.getElementById('generateBtn');
    const status = document.getElementById('status');
    const preview = document.getElementById('preview');

    uploadArea.addEventListener('click', () => fileInput.click());
    uploadArea.addEventListener('dragover', (e) => {
      e.preventDefault();
      uploadArea.style.borderColor = '#007bff';
    });
    uploadArea.addEventListener('dragleave', () => {
      uploadArea.style.borderColor = '#ccc';
    });
    uploadArea.addEventListener('drop', (e) => {
      e.preventDefault();
      uploadArea.style.borderColor = '#ccc';
      if (e.dataTransfer.files.length) {
        handleFile(e.dataTransfer.files[0]);
      }
    });
    fileInput.addEventListener('change', (e) => {
      if (e.target.files.length) {
        handleFile(e.target.files[0]);
      }
    });

    function handleFile(file) {
      if (!file.type.startsWith('image/')) {
        showStatus('Please upload an image file.', 'error');
        return;
      }
      uploadedFile = file;
      uploadArea.innerHTML = `<p>βœ… ${file.name}</p>`;
      generateBtn.disabled = false;
      
      // Show preview
      const reader = new FileReader();
      reader.onload = (e) => {
        preview.innerHTML = `<img src="${e.target.result}" alt="Preview">`;
      };
      reader.readAsDataURL(file);
    }

    function showStatus(message, type) {
      status.innerHTML = `<div class="${type}">${message}</div>`;
    }

    async function generatePassportPhoto() {
      if (!uploadedFile) {
        showStatus('Please upload a photo first.', 'error');
        return;
      }

      const country = document.getElementById('country').value;
      const spec = countrySpecs[country];

      generateBtn.disabled = true;
      showStatus('⏳ Processing photo...', 'success');

      try {
        // Step 1: Remove background and set to required color
        const editPrompt = `Remove the background completely. Set the background to a solid ${spec.name} passport photo background color (${spec.background}). Keep the face centered and natural. Ensure good lighting on the face. Neutral expression, eyes clearly visible.`;
        
        const editResult = await AiPass.editImage({
          imageFile: uploadedFile,
          prompt: editPrompt,
          model: 'gemini/gemini-3-pro-image-preview'
        });

        // Step 2: Download edited image
        const editedImageUrl = editResult.data[0].url;
        const editedImageBlob = await fetch(editedImageUrl).then(r => r.blob());
        const editedImageFile = new File([editedImageBlob], 'edited.png', { type: 'image/png' });

        // Step 3: Display result
        preview.innerHTML = `
          <h3>βœ… Passport Photo Ready!</h3>
          <img src="${editedImageUrl}" alt="Passport Photo">
          <p style="margin-top: 15px;">
            <a href="${editedImageUrl}" download="passport-photo-${country}.png" 
               style="display: inline-block; padding: 10px 20px; background: #28a745; color: white; text-decoration: none; border-radius: 6px;">
              ⬇️ Download Print-Ready Photo
            </a>
          </p>
          <p style="font-size: 14px; color: #666; margin-top: 10px;">
            Specifications: ${spec.width}x${spec.height}px (${spec.name} standard)
          </p>
        `;

        showStatus('βœ… Passport photo generated successfully!', 'success');

      } catch (error) {
        console.error('Error:', error);
        showStatus('❌ Error generating passport photo. Please try again.', 'error');
      } finally {
        generateBtn.disabled = false;
      }
    }

    generateBtn.addEventListener('click', generatePassportPhoto);
  </script>
</body>
</html>

Step 4: Deploy Your App

You have two deployment options:

Option A: Self-Host Anywhere

Since the SDK handles all billing and auth, you can host your app anywhere:

  • GitHub Pages (free)
  • Netlify (free tier)
  • Vercel (free tier)
  • Your own server

Just upload your index.html and it will work - no backend needed!

Option B: Publish on AI Pass Catalog

  1. Go to Developer Dashboard
  2. Click Create New App
  3. Choose HOSTED type
  4. Paste your HTML code
  5. Set slug, name, description
  6. Click Create
  7. Test at https://aipass.one/apps/your-slug
  8. Click Publish when ready

AI Pass gives you a shareable URL plus an embed button (</>) for iframe embedding.

How the Revenue Model Works

With AI Pass SDK:

  1. Users authenticate via OAuth2 popup (no API keys needed)
  2. Users pay directly to AI Pass for API usage
  3. You earn 50% commission on every API call
  4. No API costs for you - ever

Example: If a user generates 10 passport photos (~$0.10 total), you earn $0.05.

Key Features Explained

requireLogin: true

This tells the SDK to automatically show a login modal when needed. No custom auth UI required - the SDK handles everything.

Client ID vs App Slug

Your Client ID (from OAuth2 Clients page) is for SDK auth and commission tracking. Your App Slug (if publishing on AI Pass) is just the URL path.

They are different! Never use your app slug as a client ID.

Embed Feature

Every AI Pass app has a </> button at bottom-right. Click it to get iframe embed code for your website.

Testing Your App

  1. Upload the HTML to your hosting
  2. Test photo upload
  3. Test country selection
  4. Verify the generated photo
  5. Check the login flow works
  6. Test download functionality

SDK Method Reference

For passport photos, you mainly use:

  • AiPass.initialize({ clientId, requireLogin }) - Setup SDK
  • AiPass.editImage({ imageFile, prompt, model }) - Edit images (remove/change background)

Full SDK docs: https://aipass.one/docs/sdk/reference.html

GPT-5 Integration (Advanced)

If using GPT-5 models for additional validation:

const validation = await AiPass.generateCompletion({
  model: 'gpt-5-mini',
  temperature: 1,
  max_tokens: 16000,
  messages: [{
    role: 'user',
    content: 'Analyze this passport photo for compliance...'
  }]
});

⚠️ Critical: GPT-5 requires temperature: 1 and max_tokens: 16000 or it rejects requests.

Next Steps

  • Add more countries
  • Add batch processing for multiple photos
  • Add print layout options (4 photos on one page)
  • Add photo quality check
  • Add save-to-disk feature

Get Started Now

Create your AI Pass account β†’

$1 free credit on signup. Build your app, earn commission.

Related: Check out the Passport Photo Maker app for a live demo.