> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cr3dentials.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Cr3dentials SDK

> Automate login flows and extract data from any authenticated platform.

The Cr3dentials SDK automates login flows and extracts data from platforms that require authentication. It works with any platform that needs a username and password, and ships with a Providus Bank strategy that captures transaction data automatically.

## Getting your API keys

You need two keys before you start: a Partner API key from Cr3dentials and a Google API key for the Gemini AI features.

<Steps>
  <Step title="Get your Partner API key">
    1. Sign up or log in to Cr3dentials at [app.cr3dentials.xyz](https://app.cr3dentials.xyz).
    2. Go to the Partner API section of the dashboard.
    3. Click "Generate New API Key" or copy your existing key.
    4. Store the key in your `.env` file.
  </Step>

  <Step title="Get your Google API key">
    1. Visit [aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey).
    2. Click "Create API Key".
    3. Copy the key and add it to your `.env` file.
  </Step>

  <Step title="Set up your environment">
    ```bash .env theme={null}
    PARTNER_API_KEY=your-cr3dentials-api-key
    GOOGLE_API_KEY=your-gemini-api-key

    # Platform credentials
    PROVIDUS_USERNAME=your-providus-username
    PROVIDUS_PASSWORD=your-providus-password
    ```
  </Step>
</Steps>

<Warning>
  Never commit your `.env` file to Git. API keys and platform credentials are secrets. Add `.env` to your `.gitignore`.
</Warning>

## Quick start

Install the SDK:

<CodeGroup>
  ```bash npm theme={null}
  npm install cr3dentials-sdk
  ```

  ```bash pnpm theme={null}
  pnpm add cr3dentials-sdk
  ```
</CodeGroup>

Basic usage, shown here with Providus Bank:

```typescript theme={null}
import { Cr3dentialsSDK } from "cr3dentials-sdk";
import "dotenv/config";

const sdk = new Cr3dentialsSDK({
  apiKey: process.env.PARTNER_API_KEY!,
  googleApiKey: process.env.GOOGLE_API_KEY,
});

await sdk.initBrowser();

// Login to any platform - using Providus Bank as example
const result = await sdk.universalLogin({
  platform: "providus_bank", // Replace with your platform identifier
  credentials: {
    username: process.env.PROVIDUS_USERNAME!,
    password: process.env.PROVIDUS_PASSWORD!,
  },
});

console.log(result.success ? "Login successful" : "Login failed");

await sdk.close();
```

## Platform identifiers

Each platform you integrate needs a unique identifier, such as `"providus_bank"`, `"your_bank"`, or `"your_platform"`.

**Available platform strategies:**

* `providus_bank`: Providus Bank (Nigeria) with transaction capture

**Adding your own platform:** You can use the SDK with any platform by:

1. Choosing a unique platform identifier.
2. Using that identifier in `universalLogin()`.
3. The SDK discovers and caches the login flow automatically.

```typescript theme={null}
// Example: Using with a custom platform
const result = await sdk.universalLogin({
  platform: "my_custom_platform", // Your unique identifier
  credentials: {
    username: process.env.USERNAME!,
    password: process.env.PASSWORD!,
  },
});
```

## Login and capture data

The SDK logs into a platform and captures relevant data during the login flow.

### Providus Bank (transaction data)

Providus Bank captures transaction data automatically during login:

```typescript theme={null}
const result = await sdk.universalLogin({
  platform: "providus_bank",
  credentials: {
    username: process.env.PROVIDUS_USERNAME!,
    password: process.env.PROVIDUS_PASSWORD!,
  },
});

if (result.success) {
  console.log(`Logged in successfully in ${result.duration}ms`);
  console.log(`Cached flow used: ${result.cachedStepsUsed}`);

  // Access captured transactions
  if (result.data?.transactions) {
    console.log(`Captured ${result.data.transactions.length} transactions`);

    result.data.transactions.slice(0, 5).forEach((tx) => {
      console.log(`${tx.traDate} - ${tx.desc}`);
      console.log(
        `Debit: ${tx.debitAmnt || "N/A"} | Credit: ${tx.creditAmnt || "N/A"}`
      );
    });
  }
}
```

### Transaction data structure

```typescript theme={null}
interface ProvidusTransaction {
  traDate: string; // Transaction date (DD/MM/YYYY)
  debitAmnt: string; // Debit amount
  creditAmnt: string; // Credit amount
  desc: string; // Transaction description
}
```

### Using with other platforms

The same pattern works for any platform:

```typescript theme={null}
const result = await sdk.universalLogin({
  platform: "your_platform_identifier",
  credentials: {
    username: process.env.YOUR_PLATFORM_USERNAME!,
    password: process.env.YOUR_PLATFORM_PASSWORD!,
  },
});

// Access platform-specific data from result.data
if (result.success && result.data) {
  console.log("Platform data:", result.data);
}
```

## Verify specific transactions (Providus Bank)

<Note>
  Transaction verification via `platformConfig` is a Providus Bank-specific feature. Other platforms may have different `platformConfig` options based on their capabilities.
</Note>

For Providus Bank, you can verify whether a specific transaction exists during login:

```typescript theme={null}
const result = await sdk.universalLogin({
  platform: "providus_bank",
  credentials: {
    username: process.env.PROVIDUS_USERNAME!,
    password: process.env.PROVIDUS_PASSWORD!,
  },
  platformConfig: {
    verifyTransaction: {
      desc: "OUTWARD TRANSFER",
      recipient: "JOHN MICHAEL DOE",
      amount: 100,
      transactionType: "debit",
      dateRange: {
        from: "15/10/2025",
        to: "15/10/2025",
      },
    },
  },
});

if (result.success && result.data?.verification) {
  console.log(`Transaction found: ${result.data.verification.found}`);
  console.log(`Total matches: ${result.data.verification.totalMatches}`);

  if (result.data.verification.found) {
    result.data.verification.matchedTransactions.forEach((tx) => {
      console.log(`${tx.traDate} - ${tx.desc} - ${tx.debitAmnt}`);
    });
  }
}
```

### Verification options

| Option            | Description                                |
| ----------------- | ------------------------------------------ |
| `desc`            | Transaction description (partial match)    |
| `recipient`       | Recipient name (partial match)             |
| `orderRef`        | Order reference (include `order:` prefix)  |
| `amount`          | Amount to verify (string or number)        |
| `transactionType` | `"debit"` or `"credit"`                    |
| `dateRange`       | `{ from: "DD/MM/YYYY", to: "DD/MM/YYYY" }` |

All criteria use AND logic, so every field you provide must match.

## Manual verification (ZK proof model)

If you do not want the SDK to log in on behalf of users, use the manual verification workflow (similar to zkp2p). In this model:

* Users log into their bank or platform themselves.
* The SDK provides a verification session and monitors for proof.
* No credentials are shared with your application.
* Users keep full control and privacy.

This approach fits:

* Privacy-focused applications
* Compliance requirements where credential sharing is restricted
* User-controlled verification flows
* Zero-knowledge proof architectures

### Using the API-only client

For manual verification workflows, use `Cr3dentialsApiClient` instead of the full SDK:

```typescript theme={null}
import { Cr3dentialsApiClient } from "cr3dentials-sdk";
import "dotenv/config";

const client = new Cr3dentialsApiClient({
  apiKey: process.env.PARTNER_API_KEY!,
});
```

<Info>
  The API-only client is lightweight and does not require browser automation dependencies.
</Info>

<Steps>
  <Step title="Get available verification types">
    Discover what types of verification are available:

    ```typescript theme={null}
    const types = await client.getVerificationTypes();
    console.log("Available verification types:", types);

    // Example response:
    // [
    //   {
    //     id: "bank_statement",
    //     name: "Bank Statement Verification",
    //     description: "Verify bank transactions"
    //   }
    // ]
    ```
  </Step>

  <Step title="Get sources for a verification type">
    Get the available platforms or sources for a specific verification type:

    ```typescript theme={null}
    const sources = await client.getSources("bank_statement");
    console.log("Available sources:", sources);

    // Example response:
    // [
    //   {
    //     id: "providus_bank",
    //     name: "Providus Bank",
    //     logo: "https://...",
    //     type: "bank"
    //   }
    // ]
    ```
  </Step>

  <Step title="Create a verification session">
    Create a session where the user will manually verify:

    ```typescript theme={null}
    const session = await client.createVerificationSession({
      type: "bank_statement",
      source: "providus_bank",
      // Optional: Add transaction verification criteria
      metadata: {
        verifyTransaction: {
          desc: "SALARY PAYMENT",
          amount: 50000,
          transactionType: "credit",
          dateRange: {
            from: "01/11/2025",
            to: "07/11/2025",
          },
        },
      },
    });

    console.log("Session created:", session.id);
    console.log("Verification URL:", session.verificationUrl);
    ```
  </Step>

  <Step title="Direct the user to the verification URL">
    Present the verification URL to your user:

    ```typescript theme={null}
    // In your application:
    // 1. Redirect user to session.verificationUrl
    // 2. User logs into their bank/platform
    // 3. User completes verification steps
    // 4. User is redirected back to your callback URL

    // Example: Express.js redirect
    res.redirect(session.verificationUrl);
    ```
  </Step>

  <Step title="Poll for verification status">
    Monitor the verification session for completion:

    ```typescript theme={null}
    // Poll every 5 seconds for status updates
    const checkStatus = async () => {
      const steps = await client.getSessionSteps(session.id);

      console.log(`Session status: ${steps.status}`);

      if (steps.status === "COMPLETED") {
        console.log("Verification completed successfully!");

        // Access verification data
        if (steps.data?.verification) {
          console.log("Transaction verified:", steps.data.verification.found);
          console.log(
            "Matched transactions:",
            steps.data.verification.matchedTransactions
          );
        }

        if (steps.data?.transactions) {
          console.log(`Captured ${steps.data.transactions.length} transactions`);
        }

        return true;
      }

      if (steps.status === "FAILED" || steps.status === "REJECTED") {
        console.error("Verification failed:", steps.message);
        return true;
      }

      return false; // Continue polling
    };

    // Poll with interval
    const pollInterval = setInterval(async () => {
      const isDone = await checkStatus();
      if (isDone) {
        clearInterval(pollInterval);
      }
    }, 5000);
    ```
  </Step>

  <Step title="Handle verification results">
    Once the verification is complete, process the results:

    ```typescript theme={null}
    const steps = await client.getSessionSteps(session.id);

    if (steps.status === "COMPLETED" && steps.data) {
      // Handle transaction data
      if (steps.data.transactions) {
        steps.data.transactions.forEach((tx) => {
          console.log(`${tx.traDate} - ${tx.desc}`);
          console.log(
            `Debit: ${tx.debitAmnt || "N/A"} | Credit: ${tx.creditAmnt || "N/A"}`
          );
        });
      }

      // Handle transaction verification results
      if (steps.data.verification) {
        if (steps.data.verification.found) {
          console.log("✅ Transaction verified successfully!");
          console.log(
            `Found ${steps.data.verification.totalMatches} matching transaction(s)`
          );

          // Process matched transactions
          steps.data.verification.matchedTransactions.forEach((tx) => {
            // Update your database, trigger webhooks, etc.
            console.log("Matched:", tx);
          });
        } else {
          console.log("❌ Transaction not found");
        }
      }
    }
    ```
  </Step>
</Steps>

### Complete manual verification example

```typescript theme={null}
import { Cr3dentialsApiClient } from "cr3dentials-sdk";
import "dotenv/config";

async function manualVerificationFlow() {
  const client = new Cr3dentialsApiClient({
    apiKey: process.env.PARTNER_API_KEY!,
  });

  try {
    // 1. Create verification session
    const session = await client.createVerificationSession({
      type: "bank_statement",
      source: "providus_bank",
      metadata: {
        verifyTransaction: {
          desc: "SALARY PAYMENT",
          amount: 50000,
          transactionType: "credit",
        },
      },
    });

    console.log("Verification URL:", session.verificationUrl);
    console.log("Direct user to this URL to complete verification");

    // 2. Poll for completion (in real app, use webhooks instead)
    let isComplete = false;
    let attempts = 0;
    const maxAttempts = 60; // 5 minutes with 5-second intervals

    while (!isComplete && attempts < maxAttempts) {
      await new Promise((resolve) => setTimeout(resolve, 5000));
      attempts++;

      const steps = await client.getSessionSteps(session.id);
      console.log(`[${attempts}] Status: ${steps.status}`);

      if (steps.status === "COMPLETED") {
        console.log("✅ Verification completed!");

        if (steps.data?.verification?.found) {
          console.log(
            `Transaction verified: ${steps.data.verification.totalMatches} match(es)`
          );
        }

        isComplete = true;
      } else if (steps.status === "FAILED" || steps.status === "REJECTED") {
        console.error("❌ Verification failed");
        isComplete = true;
      }
    }

    if (!isComplete) {
      console.log("⏱️ Verification timeout - session still pending");
    }
  } catch (error) {
    console.error("Error:", error);
  }
}

manualVerificationFlow();
```

### Using webhooks (recommended for production)

Instead of polling, configure webhooks to receive real-time updates:

```typescript theme={null}
// When creating a session, provide a webhook URL
const session = await client.createVerificationSession({
  type: "bank_statement",
  source: "providus_bank",
  callbackUrl: "https://your-app.com/webhooks/verification",
  metadata: {
    // Your verification criteria
  },
});

// In your webhook endpoint:
app.post("/webhooks/verification", (req, res) => {
  const { sessionId, status, data } = req.body;

  if (status === "COMPLETED") {
    // Process verification results
    console.log("Verification completed for session:", sessionId);
    console.log("Data:", data);

    // Update your database, notify user, etc.
  }

  res.status(200).send("OK");
});
```

### Session status reference

| Status       | Meaning                                  |
| ------------ | ---------------------------------------- |
| `PENDING`    | Session created, waiting for user action |
| `PROCESSING` | User is completing verification steps    |
| `COMPLETED`  | Verification successful, data available  |
| `REVIEWING`  | Manual review required                   |
| `FAILED`     | Verification failed (technical error)    |
| `REJECTED`   | Verification rejected (criteria not met) |
| `EXPIRED`    | Session expired before completion        |

## Extract custom data

After login, you can perform additional actions and extract custom data from any platform using natural language.

### Providus Bank

```typescript theme={null}
import { z } from "zod";

const loginResult = await sdk.universalLogin({
  platform: "providus_bank",
  credentials: {
    username: process.env.PROVIDUS_USERNAME!,
    password: process.env.PROVIDUS_PASSWORD!,
  },
});

if (loginResult.success) {
  const page = await sdk.getPage();

  if (page) {
    // Perform actions using natural language
    await page.act({ action: "Click on Account Summary" });

    // Extract custom data
    const accountInfo = await page.extract({
      instruction: "Extract account balance and account number",
      schema: z.object({
        balance: z.string(),
        accountNumber: z.string(),
        accountName: z.string(),
      }),
    });

    console.log(`Account: ${accountInfo.accountNumber}`);
    console.log(`Balance: ${accountInfo.balance}`);

    // Multi-step tasks
    await page.act({ action: "Navigate to transaction history" });
    await page.act({ action: "Click Export to CSV button" });
  }
}
```

### Using with any platform

The same approach works for any platform. Change the platform identifier and the extraction logic:

```typescript theme={null}
import { z } from "zod";

const loginResult = await sdk.universalLogin({
  platform: "your_platform", // Your platform identifier
  credentials: {
    username: process.env.USERNAME!,
    password: process.env.PASSWORD!,
  },
});

if (loginResult.success) {
  const page = await sdk.getPage();

  if (page) {
    // Navigate and perform actions
    await page.act({ action: "Click on Dashboard" });

    // Extract platform-specific data
    const data = await page.extract({
      instruction: "Extract the data you need",
      schema: z.object({
        // Define your data structure
        field1: z.string(),
        field2: z.number(),
      }),
    });

    console.log("Extracted data:", data);
  }
}
```

## Configuration

### SDK configuration

```typescript theme={null}
const sdk = new Cr3dentialsSDK({
  apiKey: process.env.PARTNER_API_KEY!, // Required
  googleApiKey: process.env.GOOGLE_API_KEY, // Required for AI features

  // Optional settings
  headless: true, // Hide browser (default: true)
  verbose: false, // Detailed logging (default: false)
  sourceName: "providus_bank", // Platform identifier for flow caching
  timeout: 30000, // API timeout (ms)
  retryAttempts: 3, // Retry attempts
  retryDelay: 1000, // Retry delay (ms)
});
```

<ParamField path="apiKey" type="string" required>
  Your Partner API key from the Cr3dentials dashboard.
</ParamField>

<ParamField path="googleApiKey" type="string" required>
  Your Google API key. Required for the AI features.
</ParamField>

<ParamField path="headless" type="boolean" default="true">
  Hide the browser UI.
</ParamField>

<ParamField path="verbose" type="boolean" default="false">
  Enable detailed logging.
</ParamField>

<ParamField path="sourceName" type="string">
  Platform identifier used for flow caching. Match it to the identifier you pass to `universalLogin()` to enable faster subsequent logins.
</ParamField>

<ParamField path="timeout" type="number">
  API timeout in milliseconds.
</ParamField>

<ParamField path="retryAttempts" type="number">
  Number of retry attempts.
</ParamField>

<ParamField path="retryDelay" type="number">
  Delay between retries in milliseconds.
</ParamField>

### Environment variables

```bash .env theme={null}
PARTNER_API_KEY=your-api-key-from-cr3dentials-dashboard
GOOGLE_API_KEY=your-gemini-api-key

# Platform credentials (example: Providus Bank)
PROVIDUS_USERNAME=your-providus-username
PROVIDUS_PASSWORD=your-providus-password

# Add credentials for other platforms as needed
YOUR_PLATFORM_USERNAME=your-username
YOUR_PLATFORM_PASSWORD=your-password
```

## Error handling

```typescript theme={null}
import {
  ApiError,
  AuthenticationError,
  AutomationError,
} from "cr3dentials-sdk";

try {
  const result = await sdk.universalLogin({
    platform: "providus_bank",
    credentials: {
      username: process.env.PROVIDUS_USERNAME!,
      password: process.env.PROVIDUS_PASSWORD!,
    },
  });

  if (!result.success) {
    console.error("Login failed:", result.message);
  }
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error("Auth failed:", error.message);
  } else if (error instanceof AutomationError) {
    console.error("Automation failed:", error.message);
  } else if (error instanceof ApiError) {
    console.error(`API error (${error.statusCode}):`, error.message);
  }
} finally {
  await sdk.close(); // Always close browser
}
```

<AccordionGroup>
  <Accordion title="AuthenticationError">
    The provided credentials were rejected by the platform. Check the username and password, and confirm the account is not locked or requiring additional verification.
  </Accordion>

  <Accordion title="AutomationError">
    The browser automation could not complete the login or extraction flow. This can happen when the platform UI changed or a step timed out.
  </Accordion>

  <Accordion title="ApiError">
    The Cr3dentials API returned an error. Read `error.statusCode` and `error.message` for details.
  </Accordion>
</AccordionGroup>

## Best practices

### Always use try/finally

```typescript theme={null}
const sdk = new Cr3dentialsSDK({ apiKey: "..." });

try {
  await sdk.initBrowser();
  const result = await sdk.universalLogin({ ... });
} finally {
  await sdk.close(); // Always close browser
}
```

### Use environment variables

```typescript theme={null}
import "dotenv/config";

const sdk = new Cr3dentialsSDK({
  apiKey: process.env.PARTNER_API_KEY!,
  googleApiKey: process.env.GOOGLE_API_KEY,
});
```

### Development vs production

```typescript theme={null}
// Development
const sdk = new Cr3dentialsSDK({
  verbose: true, // See detailed logs
  headless: false, // Show browser UI
});

// Production
const sdk = new Cr3dentialsSDK({
  verbose: false, // Minimal logging
  headless: true, // Hide browser
});
```

### Monitor live sessions

```typescript theme={null}
await sdk.initBrowser();

const sessionId = sdk.getBrowserSessionId();
if (sessionId) {
  console.log(`Watch: https://browserbase.com/sessions/${sessionId}`);
}
```

### Reuse the SDK instance

```typescript theme={null}
try {
  await sdk.initBrowser();

  // Login to Providus Bank
  const result = await sdk.universalLogin({
    platform: "providus_bank",
    credentials: {
      username: process.env.PROVIDUS_USERNAME!,
      password: process.env.PROVIDUS_PASSWORD!,
    },
  });

  // Perform multiple actions in same session
  const page = await sdk.getPage();
  if (page) {
    await page.act({ action: "Click on Account Details" });
    await page.act({ action: "Download statement" });
    await page.act({ action: "Navigate to profile settings" });
  }
} finally {
  await sdk.close();
}
```

## Complete examples

All examples below use Providus Bank as the platform. To use another platform, change the `platform` identifier and adjust the data extraction logic for that platform's data structure.

### Example 1: Basic login and data capture

```typescript theme={null}
import "dotenv/config";
import { Cr3dentialsSDK } from "cr3dentials-sdk";

const sdk = new Cr3dentialsSDK({
  apiKey: process.env.PARTNER_API_KEY!,
  googleApiKey: process.env.GOOGLE_API_KEY,
});

try {
  await sdk.initBrowser();

  const result = await sdk.universalLogin({
    platform: "providus_bank", // Change to your platform
    credentials: {
      username: process.env.PROVIDUS_USERNAME!,
      password: process.env.PROVIDUS_PASSWORD!,
    },
  });

  if (result.success && result.data?.transactions) {
    console.log(`Captured ${result.data.transactions.length} transactions`);
    result.data.transactions.slice(0, 3).forEach((tx) => {
      console.log(`${tx.traDate} - ${tx.desc}`);
    });
  }
} finally {
  await sdk.close();
}
```

### Example 2: Transaction verification

```typescript theme={null}
const result = await sdk.universalLogin({
  platform: "providus_bank",
  credentials: {
    username: process.env.PROVIDUS_USERNAME!,
    password: process.env.PROVIDUS_PASSWORD!,
  },
  platformConfig: {
    verifyTransaction: {
      desc: "OUTWARD TRANSFER",
      recipient: "JOHN MICHAEL DOE",
      amount: 100,
      transactionType: "debit",
    },
  },
});

if (result.success && result.data?.verification?.found) {
  console.log(
    `Found ${result.data.verification.totalMatches} matching transactions`
  );
  result.data.verification.matchedTransactions.forEach((tx) => {
    console.log(`${tx.traDate} - ${tx.desc} - ${tx.debitAmnt}`);
  });
}
```

### Example 3: Custom data extraction

```typescript theme={null}
import { z } from "zod";

const loginResult = await sdk.universalLogin({
  platform: "providus_bank",
  credentials: {
    username: process.env.PROVIDUS_USERNAME!,
    password: process.env.PROVIDUS_PASSWORD!,
  },
});

if (loginResult.success) {
  const page = await sdk.getPage();

  if (page) {
    // Navigate to account details
    await page.act({ action: "Click on Account Details" });

    // Extract account information
    const accountDetails = await page.extract({
      instruction: "Extract account balance, account number, and account type",
      schema: z.object({
        balance: z.string(),
        accountNumber: z.string(),
        accountType: z.string(),
        accountName: z.string(),
      }),
    });

    console.log(`Account: ${accountDetails.accountNumber}`);
    console.log(`Balance: ${accountDetails.balance}`);
    console.log(`Type: ${accountDetails.accountType}`);

    // Perform additional actions
    await page.act({ action: "Navigate to statement download page" });
    await page.act({ action: "Select last 3 months" });
    await page.act({ action: "Click Download PDF button" });
  }
}
```
