How to Automate Vendor Registration on Etimad

Sep 9

How to Automate Vendor Registration on Etimad (2025)

Meta description: Step-by-step guide to reliably automate Etimad vendor registration using the Anchor Browser SDK—handles OTP, pop-ups, timeouts, and file uploads with audit logs.

TL;DR

Use Anchor Browser’s verified Chromium sessions + Playwright helpers to script Etimad’s vendor-registration flow with retries, OTP handling, and audit trails. Copy the code, swap selectors, and run.

Prerequisites

  • Anchor Browser account & API key (project-level).
  • Node 18+.
  • Dedicated proxy for your region (recommended).
  • Etimad account with the right role; comply with portal terms and all procurement rules.

npm i @anchorbrowser/sdk playwright

Create .env:

ANCHOR_API_KEY=...           # from console
ANCHOR_REGION=me-central-1   # example; pick closest
PROXY_URL=http://user:pass@host:port   # optional but recommended

The approach (reliable pattern)

  1. Start a verified browser session (humanized™ fingerprint, bot-check safe).
  2. Navigate & log in (SSO/OTP with human-in-the-loop or SMS inbox integration).
  3. Fill the registration steps via resilient selectors (labels/roles, not brittle CSS).
  4. Upload documents with chunked upload + retry on 413/timeout.
  5. Submit & capture evidence (screenshots, HTML snapshot, network log).
  6. Emit audit: who, when, which fields were touched (no secrets).

Code: Etimad vendor registration with Anchor SDK

Notes

  • Replace placeholder selectors with the current labels in Etimad.
  • The OTP handler below is an example—swap in your SMS/Email source as needed.
  • Uses Playwright routed through Anchor’s verified session.
// etimad-register.ts
import 'dotenv/config';
import { Anchor } from '@anchorbrowser/sdk';
import { chromium, Page } from 'playwright';

type VendorProfile = {
 companyName: string;
 crNumber: string;        // Commercial Registration
 taxNumber: string;
 iban: string;
 contactName: string;
 contactEmail: string;
 contactPhone: string;
 docs: {
   crPdf: string;         // ./docs/cr.pdf
   taxCertPdf: string;    // ./docs/tax.pdf
   ibanLetterPdf: string; // ./docs/iban.pdf
 };
};

const profile: VendorProfile = {
 companyName: 'Anchor Forge Ltd.',
 crNumber: '1010XXXXXX',
 taxNumber: '3XX-XX-XXXXXX',
 iban: 'SA44 2000 0000 0000 0000 0000',
 contactName: 'Idan Raman',
 contactEmail: 'ops@anchorbrowser.io',
 contactPhone: '+9665XXXXXXXX',
 docs: {
   crPdf: './docs/cr.pdf',
   taxCertPdf: './docs/tax.pdf',
   ibanLetterPdf: './docs/iban.pdf',
 },
};

async function main() {
 const anchor = new Anchor({
   apiKey: process.env.ANCHOR_API_KEY!,
   region: process.env.ANCHOR_REGION || 'me-central-1',
   defaults: {
     extraStealth: true,
     proxyUrl: process.env.PROXY_URL,   // optional
     recordVideo: false,
     snapshotOnError: true,
   },
 });

 // 1) Start a verified browser session
 const session = await anchor.sessions.start({
   label: 'etimad-vendor-registration',
   keepAliveSeconds: 900, // extend if needed during OTP
   metadata: { portal: 'etimad', flow: 'vendor-registration' },
 });

 // 2) Attach Playwright to the Anchor session
 const wsEndpoint = await session.playwright.connectWsEndpoint();
 const browser = await chromium.connectOverCDP(wsEndpoint);
 const context = browser.contexts()[0] || await browser.newContext({
   viewport: { width: 1280, height: 800 },
   userAgent: await session.fingerprint.userAgent(), // humanized UA
 });
 const page = await context.newPage();

 // Helpers
 const safeClick = (p: Page, sel: string) =>
   p.getByRole('button', { name: sel }).or(p.getByText(sel, { exact: true })).click();

 const typeByLabel = async (p: Page, label: string, value: string) => {
   const ctl = p.getByLabel(label).first();
   await ctl.waitFor({ state: 'visible', timeout: 15000 });
   await ctl.fill(value, { timeout: 15000 });
 };

 // 3) Login flow (email + password + OTP)
 await page.goto('https://login.etimad.sa/', { waitUntil: 'domcontentloaded', timeout: 90000 });

 // Example selectors—replace with the current labels on the portal
 await typeByLabel(page, 'Email', process.env.ETIMAD_EMAIL!);
 await typeByLabel(page, 'Password', process.env.ETIMAD_PASSWORD!);
 await safeClick(page, 'Sign in');

 // OTP (human-in-the-loop or mailbox/SMS poll)
 const code = await anchor.human.prompt({
   title: 'Enter Etimad OTP',
   description: 'Check registered device/email and paste the 6-digit code.',
   mask: false,
   timeoutSeconds: 180,
 });
 await typeByLabel(page, 'One-time code', code.value);
 await safeClick(page, 'Verify');

 // 4) Navigate to: Vendor Registration
 await page.waitForURL(/dashboard|home/, { timeout: 120000 });
 await page.getByRole('link', { name: /Vendors?/ }).click();
 await page.getByRole('link', { name: /Register/i }).click();

 // 5) Fill company details
 await typeByLabel(page, 'Company Name (English)', profile.companyName);
 await typeByLabel(page, 'Commercial Registration Number', profile.crNumber);
 await typeByLabel(page, 'VAT / Tax Number', profile.taxNumber);
 await typeByLabel(page, 'IBAN', profile.iban);

 // 6) Contact details
 await typeByLabel(page, 'Contact Person Name', profile.contactName);
 await typeByLabel(page, 'Contact Email', profile.contactEmail);
 await typeByLabel(page, 'Contact Phone', profile.contactPhone);

 // 7) Upload docs (robust uploads with retry)
 const uploadWithRetry = async (label: string, path: string) => {
   for (let i = 0; i < 3; i++) {
     try {
       const input = page.getByLabel(label).or(page.locator('input[type="file"]').nth(0));
       await input.setInputFiles(path, { timeout: 30000 });
       await page.getByText(/uploaded|success/i).first().waitFor({ timeout: 10000 });
       return;
     } catch (e) {
       if (i === 2) throw e;
       await page.waitForTimeout(1500 + i * 1000);
     }
   }
 };

 await uploadWithRetry('Commercial Registration PDF', profile.docs.crPdf);
 await uploadWithRetry('Tax Certificate PDF', profile.docs.taxCertPdf);
 await uploadWithRetry('IBAN Letter PDF', profile.docs.ibanLetterPdf);

 // 8) Consent & submit
 await page.getByLabel(/I confirm/i).check({ timeout: 10000 });
 await safeClick(page, 'Submit');

 // 9) Evidence & audit
 const receipt = await page.locator('text=Reference No').first().textContent().catch(() => null);
 await session.audit.log('etimad.vendor_registration.submitted', {
   reference: receipt ?? 'pending',
   fieldsTouched: [
     'companyName','crNumber','taxNumber','iban',
     'contactName','contactEmail','contactPhone'
   ],
 });

 await session.artifacts.saveScreenshot(page, { label: 'post-submit' });
 await session.artifacts.saveDomSnapshot(page, { label: 'post-submit-html' });
 await session.complete({ status: 'success', note: 'Vendor registration submitted' });

 await browser.close();
}

main().catch(async (err) => {
 console.error(err);
 // Rich failure evidence
 // (Anchor automatically adds last screenshot/snapshot when snapshotOnError=true)
 process.exit(1);
});

Troubleshooting (quick)

  • OTP delays/timeouts → increase keepAliveSeconds, allow manual pause via anchor.human.confirm().
  • Upload fails / 413 → ensure file size under limit; fall back to chunked upload if portal supports it; retry with backoff.
  • Selector breaks after UI update → prefer getByLabel, getByRole, and text-based locators; avoid brittle #ids.
  • 429 / anti-bot friction → use Anchor verified sessions, humanized timings (page.waitForTimeout(±random)), stable IP/proxy, and retry on 429 with jitter.

Compliance & audit

  • Respect the portal’s terms and applicable procurement regulations.
  • Store only business data; keep OTP/secrets in your vault.
  • Enable Anchor’s action log + artifact snapshots for audits.

FAQ (short)

Can this run headless? Yes; verified sessions work headful or headless. Prefer headful for flaky flows.
How do we pass OTP without a human? Use your SMS/Email provider API; feed the code into typeByLabel (or keep human-approval).
Will this survive UI changes? Use label/role selectors and keep a nightly smoke test; update selectors via a small map.

CTA: Want this as a ready template (with current selectors)? Spin up an Etimad Vendor Registration session template in Anchor Browser and plug in your env vars.

Other hubs

See all
No hubs found

Stay ahead in browser automation

We respect your inbox. Privacy policy

Welcome aboard! Thanks for signing up
Oops! Something went wrong while submitting the form.