# JavaScript Examples

Practical JavaScript code examples for integrating with the Zaits API in various scenarios.

## Setup

```bash
npm install zaits-sdk
```

```javascript
const { ZaitsClient } = require("zaits-sdk");
const client = new ZaitsClient({ apiKey: "your-api-key" });
```

## Face Recognition Examples

### Basic Face Verification

```javascript
async function verifyFaces(image1, image2) {
  try {
    const result = await client.face.verify(image1, image2);

    if (result.verified) {
      console.log(
        `Faces match! Confidence: ${(result.confidence * 100).toFixed(1)}%`
      );
    } else {
      console.log(
        `Faces don't match. Confidence: ${(result.confidence * 100).toFixed(
          1
        )}%`
      );
    }

    return result;
  } catch (error) {
    console.error("Verification failed:", error.message);
    throw error;
  }
}

// Usage
verifyFaces("./photos/john_1.jpg", "./photos/john_2.jpg");
```

### User Registration with Face Verification

```javascript
const multer = require("multer");
const express = require("express");
const app = express();

const upload = multer({ dest: "uploads/" });

app.post(
  "/register",
  upload.fields([{ name: "photo1" }, { name: "photo2" }]),
  async (req, res) => {
    try {
      const { name, email } = req.body;
      const photo1 = req.files.photo1[0];
      const photo2 = req.files.photo2[0];

      // Verify both photos are of the same person
      const verification = await client.face.verify(photo1.path, photo2.path);

      if (!verification.verified || verification.confidence < 0.8) {
        return res.status(400).json({
          error: "Face verification failed",
          message: "Please provide two clear photos of the same person",
        });
      }

      // Check for liveness (anti-spoofing)
      const liveness = await client.face.liveness(photo1.path);

      if (!liveness.isLive || liveness.confidence < 0.7) {
        return res.status(400).json({
          error: "Liveness check failed",
          message:
            "Please provide a live photo, not a screenshot or printed image",
        });
      }

      // Register user
      const user = await createUser({
        name,
        email,
        profilePhoto: photo1.path,
        verificationScore: verification.confidence,
        livenessScore: liveness.confidence,
      });

      res.json({
        success: true,
        user: { id: user.id, name: user.name, email: user.email },
        verification: {
          verified: true,
          confidence: verification.confidence,
          liveness: liveness.confidence,
        },
      });
    } catch (error) {
      console.error("Registration error:", error);
      res.status(500).json({ error: "Registration failed" });
    }
  }
);
```

### Real-time Face Analysis with Webcam

```javascript
// Client-side: Real-time face analysis
class FaceAnalyzer {
  constructor() {
    this.video = document.getElementById("webcam");
    this.canvas = document.getElementById("canvas");
    this.ctx = this.canvas.getContext("2d");
    this.analyzing = false;
  }

  async startWebcam() {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: { width: 640, height: 480 },
      });
      this.video.srcObject = stream;
      this.video.play();

      // Analyze every 2 seconds
      setInterval(() => this.analyzeFrame(), 2000);
    } catch (error) {
      console.error("Webcam access failed:", error);
    }
  }

  async analyzeFrame() {
    if (this.analyzing) return;

    this.analyzing = true;

    try {
      // Capture frame from video
      this.canvas.width = this.video.videoWidth;
      this.canvas.height = this.video.videoHeight;
      this.ctx.drawImage(this.video, 0, 0);

      // Convert to blob and send to server
      const blob = await new Promise((resolve) => {
        this.canvas.toBlob(resolve, "image/jpeg", 0.8);
      });

      const formData = new FormData();
      formData.append("image", blob);

      const response = await fetch("/api/analyze-face", {
        method: "POST",
        body: formData,
      });

      const analysis = await response.json();
      this.displayResults(analysis);
    } catch (error) {
      console.error("Analysis failed:", error);
    } finally {
      this.analyzing = false;
    }
  }

  displayResults(analysis) {
    const results = document.getElementById("results");
    results.innerHTML = `
      <div class="analysis-result">
        <p><strong>Age:</strong> ${analysis.age}</p>
        <p><strong>Gender:</strong> ${analysis.gender.prediction} (${(
      analysis.gender.confidence * 100
    ).toFixed(1)}%)</p>
        <p><strong>Emotion:</strong> ${analysis.emotion.dominantEmotion}</p>
        <p><strong>Face Quality:</strong> ${
          analysis.faceRegion ? "Good" : "Poor"
        }</p>
      </div>
    `;
  }
}

// Server-side endpoint
app.post("/api/analyze-face", upload.single("image"), async (req, res) => {
  try {
    const analysis = await client.face.analyze(req.file.path, {
      actions: ["age", "gender", "emotion"],
      returnFaceRegion: true,
    });

    res.json(analysis);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});
```

## OCR Examples

### ID Extraction (INE)

```javascript
const FormData = require('form-data');
const fs = require('fs');
const fetch = require('node-fetch');

const API_KEY = 'YOUR_API_KEY';
const BASE_URL = 'https://api.zaits.net';

// Extract INE — front + back (recommended)
async function extractINE(frontPath, backPath) {
  const form = new FormData();
  form.append('front', fs.createReadStream(frontPath));
  if (backPath) form.append('back', fs.createReadStream(backPath));
  form.append('document_type', 'ine');

  const response = await fetch(`${BASE_URL}/v1/ocr/extract/id`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${API_KEY}`, ...form.getHeaders() },
    body: form,
  });

  const result = await response.json();

  if (!result.success) {
    throw new Error(result.message || result.error);
  }

  console.log('Nombre:',   result.fields.surname);
  console.log('CURP:',     result.fields.curp);
  console.log('Domicilio:', result.fields.address);
  console.log('MRZ:',      result.validation.mrz);       // 'ok' | 'failed' | 'not_present'
  console.log('Vencido:',  result.validation.expired);   // true | false | null

  return result;
}
```

### ID Extraction with Authenticity

```javascript
// Include authenticity verification in the same request (extra credit charge)
async function extractINEWithAuthenticity(frontPath, backPath) {
  const form = new FormData();
  form.append('front', fs.createReadStream(frontPath));
  form.append('back', fs.createReadStream(backPath));
  form.append('document_type', 'ine');
  form.append('include_authenticity', 'true');

  const response = await fetch(`${BASE_URL}/v1/ocr/extract/id`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${API_KEY}`, ...form.getHeaders() },
    body: form,
  });

  const result = await response.json();

  // Authenticity is merged into the response when include_authenticity=true
  if (result.authenticity) {
    const auth = result.authenticity.validity_check;
    console.log('Auténtico:', auth.is_authentic);
    console.log('Confianza:', auth.confidence);
    console.log('Manipulación:', auth.tampering_detected);
  }

  return result;
}
```

### Passport Extraction

```javascript
async function extractPassport(imagePath) {
  const form = new FormData();
  form.append('front', fs.createReadStream(imagePath));
  form.append('document_type', 'passport');

  const response = await fetch(`${BASE_URL}/v1/ocr/extract/id`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${API_KEY}`, ...form.getHeaders() },
    body: form,
  });

  return response.json();
}
```

### Address Proof (CFE / TELMEX / IZZI)

```javascript
// Extract CFE utility bill
async function extractAddressProof(imagePath, type = 'cfe') {
  const form = new FormData();
  form.append('image', fs.createReadStream(imagePath));
  form.append('document_type', type);  // 'cfe' | 'telmex' | 'izzi'

  const response = await fetch(`${BASE_URL}/v1/ocr/extract/document`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${API_KEY}`, ...form.getHeaders() },
    body: form,
  });

  const result = await response.json();

  if (!result.success) {
    throw new Error(result.message || result.error);
  }

  console.log('Nombre:',    result.fields.name);
  console.log('Domicilio:', result.fields.fullAddress);
  console.log('CP:',        result.fields.zipCode);
  console.log('Total:',     result.fields.totalPayment);

  return result;
}

// Usage
extractAddressProof('./recibo_cfe.jpg', 'cfe');
extractAddressProof('./recibo_telmex.jpg', 'telmex');
```

### Batch ID Verification

```javascript
// Process multiple IDs concurrently
async function batchExtractIDs(documents, concurrency = 3) {
  const results = [];

  for (let i = 0; i < documents.length; i += concurrency) {
    const batch = documents.slice(i, i + concurrency);

    const batchResults = await Promise.allSettled(
      batch.map(({ front, back, type }) => extractINE(front, back, type))
    );

    batchResults.forEach((result, idx) => {
      if (result.status === 'fulfilled') {
        results.push({ ...result.value, file: batch[idx].front });
      } else {
        results.push({ error: result.reason.message, file: batch[idx].front });
      }
    });
  }

  return results;
}

// Usage
const documents = [
  { front: './user1_frente.jpg', back: './user1_reverso.jpg', type: 'ine' },
  { front: './user2_frente.jpg', back: './user2_reverso.jpg', type: 'ine' },
  { front: './user3_pasaporte.jpg', type: 'passport' },
];

batchExtractIDs(documents, 3).then((results) => {
  const ok      = results.filter((r) => !r.error);
  const failed  = results.filter((r) => r.error);
  console.log(`Processed: ${ok.length} OK, ${failed.length} failed`);
});
```

## Document Signing Examples

### Contract Signing Workflow

```javascript
class ContractSigningService {
  constructor(zaitsClient) {
    this.client = zaitsClient;
  }

  async createSigningRequest(contractData) {
    try {
      const signingRequest = await this.client.signing.create({
        document: contractData.filePath,
        signers: contractData.signers,
        title: contractData.title,
        message: contractData.message,
        expiresIn: "30d",
        requiredFields: [
          { name: "signature", type: "signature", required: true },
          { name: "date", type: "date", required: true },
          { name: "title", type: "text", required: false },
        ],
      });

      console.log("Signing request created");
      console.log(`📋 Document ID: ${signingRequest.documentId}`);
      console.log(`Signing URL: ${signingRequest.signingUrl}`);

      // Send notifications to signers
      await this.notifySigners(contractData.signers, signingRequest.signingUrl);

      return signingRequest;
    } catch (error) {
      console.error("Failed to create signing request:", error);
      throw error;
    }
  }

  async checkSigningStatus(documentId) {
    try {
      const status = await this.client.signing.getStatus(documentId);

      console.log(`Document ${documentId} status: ${status.status}`);

      status.signers.forEach((signer, index) => {
        const statusLabel =
          signer.status === "signed"
            ? "[Signed]"
            : signer.status === "pending"
            ? "[Pending]"
            : "[Failed]";
        console.log(`${statusLabel} ${signer.name}: ${signer.status}`);
      });

      return status;
    } catch (error) {
      console.error("Failed to check signing status:", error);
      throw error;
    }
  }

  async downloadSignedDocument(documentId, outputPath) {
    try {
      const signedDoc = await this.client.signing.download(documentId);

      await fs.writeFile(outputPath, signedDoc);
      console.log(`📁 Signed document saved to: ${outputPath}`);

      return outputPath;
    } catch (error) {
      console.error("Failed to download signed document:", error);
      throw error;
    }
  }

  async notifySigners(signers, signingUrl) {
    // Send email notifications (implement with your email service)
    const emailPromises = signers.map((signer) =>
      this.sendSigningEmail(signer.email, signer.name, signingUrl)
    );

    await Promise.all(emailPromises);
    console.log(`📧 Notifications sent to ${signers.length} signers`);
  }

  async sendSigningEmail(email, name, signingUrl) {
    // Implement with your email service (SendGrid, AWS SES, etc.)
    console.log(`📧 Sending signing invitation to ${name} (${email})`);
    // ... email implementation
  }
}

// Usage
async function processContract() {
  const signingService = new ContractSigningService(client);

  const contractData = {
    filePath: "./contracts/service_agreement.pdf",
    title: "Service Agreement Contract",
    message: "Please review and sign this service agreement.",
    signers: [
      {
        name: "John Doe",
        email: "john@example.com",
        role: "client",
      },
      {
        name: "Jane Smith",
        email: "jane@company.com",
        role: "service_provider",
      },
    ],
  };

  try {
    // Create signing request
    const signingRequest = await signingService.createSigningRequest(
      contractData
    );

    // Monitor signing progress
    const monitorSigning = setInterval(async () => {
      const status = await signingService.checkSigningStatus(
        signingRequest.documentId
      );

      if (status.status === "completed") {
        console.log("🎉 All parties have signed the contract!");

        // Download signed document
        await signingService.downloadSignedDocument(
          signingRequest.documentId,
          "./signed_contracts/service_agreement_signed.pdf"
        );

        clearInterval(monitorSigning);
      }
    }, 30000); // Check every 30 seconds
  } catch (error) {
    console.error("Contract processing failed:", error);
  }
}

processContract();
```

***

**Next:** [Python Examples](/api/code-examples/python.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zaits.gitbook.io/api/code-examples/javascript.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
