Skip to content

Freelancers ​

A Saudi merchant may trade on a commercial registration or on a freelance certificate (وثيقة العمل الحر). Verity onboards both.

Two documents, not two shapes of one ​

They are validated differently, verified by different registries, and say different things about who is contracting. So intake asks which one this merchant holds rather than inferring it from the shape of a number.

Commercial registrationFreelance certificate
Number10 digits, starting with 7FL-013988291
Issued toA company or establishmentA person
Verified byWathqLean Tech, against the Ministry's records
Also needs—The holder's national ID

Why this is not one field with a looser rule

Before this, a freelancer was either refused at intake — the certificate number does not match the unified-CR format — or entered with the format check switched off, which left no record of which kind of document the number even was.

A system that infers which document it holds will infer wrong eventually, and the consequence is a certificate number sent to the commercial registry. That returns not-found, and not-found reads as "this registration failed" — a statement about the merchant — when the truth is we queried the wrong registry.

Creating one ​

Choose Freelance certificate (وثيقة العمل الحر) on the merchant form, then give the certificate number and the national ID of the person it was issued to.

Both, always. Lean's verification takes FREELANCER_NUMBER and NATIONAL_ID together — neither alone identifies the document — so a freelancer without a national ID is a registration nothing can ever check. It is refused by the form, by the API, and by a database constraint.

Citizens only

A freelance certificate is issued to Saudi nationals, so the national ID must begin with 1. An Iqama (2…) is not a different format here — it is a person who cannot hold this document, and accepting one would send an application that must fail to the registry.

The certificate number rule is deliberately loose, and the FL- prefix optional: one documented example is not a specification, and a merchant reading the number off their certificate may or may not include it. Lean's verification is the real check on whether the document exists.

What runs at intake ​

The certificate check runs where the account configured a commercial registration — substituted, not added. Everything else the account requires at intake, such as screening, runs unchanged.

A country with no freelancer provider keeps the registration check rather than requiring one that cannot run there. Only Saudi Arabia has one; there is no Bahraini equivalent integrated.

What the check answers ​

Two questions, and both must hold:

OwnershipDoes this certificate belong to the national ID we asked about?
StatusIs that certificate in force?

Either alone misleads. A genuine, active certificate belonging to somebody else passes the second and fails the first — and onboarding against another person's document is exactly the outcome this pairing exists to prevent.

Lean publishes a closed list of statuses. Only ACTIVE is a document in force:

ACTIVE · PENDING · CANCELED · REVOKED · EXPIRED · REJECTED

A certificate runs for one year before renewal, so the expiry date underneath is checked as well as the word: an ACTIVE certificate whose expiry has passed does not verify.

An unrecognised answer is not a pass

A status outside that list, or a response that answers neither question, reports unknown — not verified and not refused.

Reporting refused would accuse a merchant on the strength of a field we failed to find. Reporting verified would clear one on the same evidence. This is the same rule every check in Verity follows; see the principle.

A person may hold several certificates. The one asked about is the one judged, matched by number — another of their certificates being active says nothing about this one.

Through the API ​

registration_type and freelancer_national_id are accepted on create and returned on read:

json
{
  "entity": {
    "registration_number": "FL-013988291",
    "registration_type": "freelance_certificate",
    "freelancer_national_id": "1099999002"
  }
}

Omit registration_type and a commercial registration is assumed, which is what every existing client already sends.

Configuration ​

Lean Tech reads from the environment. Nothing is committed:

LEANTECH_AUTH_URL       the OAuth2 token host
LEANTECH_BASE_URL       https://sandbox.sa.leantech.me or https://api2.sa.leantech.me
LEANTECH_CLIENT_ID
LEANTECH_CLIENT_SECRET
LEANTECH_SCOPE          optional
LEANTECH_TIMEOUT        optional, seconds

Which Lean environment an environment talks to is set by LEANTECH_BASE_URL rather than switched on Rails.env — that is a deployment decision, not one this code should assume.

Not yet exercised against the live API

Everything up to the HTTP call is built and tested, but no verification has been made against Lean. The response mapping is written from their published contract rather than from a response we have seen, and the token endpoint is still to be supplied.

Until one real verification has run, treat a live check as unproven. It will most likely report unknown, which is the correct failure — but not a useful one.

What it costs ​

Unknown. Lean is not on the rate sheet Verity's cost figures were transcribed from, so its calls are priced as unpriced rather than as nothing — an unknown price is never zero.