E-Contracting

Create and sign FRA-compliant digital contracts

View as Markdown

The e-contracting flow replaces paper contracts with end-to-end digital agreements — legally compliant under the Financial Regulatory Authority (FRA) of Egypt. Vlens handles form collection, document generation, OTP-based customer signing, and service-provider countersignature.


Prerequisites

Before creating a contract, the user must:

  • Be registered in Vlens (have a user access token)
  • Have a verified digital identity (isDigitalIdentityVerified: true)

Contract lifecycle

Signing flow detail


Step 1 — Discover product types

$curl "https://api.vlenseg.com/api/BusinessRequest/GetAllRequestTypes" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer USER_TOKEN"

Use the returned id (e.g. 414) in steps 2 and 4.


Step 2 — Get form fields

$curl "https://api.vlenseg.com/api/BusinessRequest/GetBusinessRequestTypeTemplateFields\
>?requestTypeId=414" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer USER_TOKEN"

The response contains requestsFields — an array of fields the user must fill in. Each field has:

PropertyDescription
keyMachine identifier — use this as the key in requestFieldsValues
displayTextEnglish label
displayArTextArabic label
typeField type: text, dropdown, date, table, etc.
availableValuesItemsOptions for dropdown fields
parentQuestionKeyShows this field only when the parent field equals parentQuestionValue

Step 3 — Check eligibility

$curl "https://api.vlenseg.com/api/BusinessRequest/CheckEligibility" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer USER_TOKEN"

isEligible is true only when all checks pass simultaneously.


Step 4 — Create the request

$curl -X POST "https://api.vlenseg.com/api/BusinessRequest/Create/414" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer USER_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "geoLocation": { "latitude": 30.0444, "longitude": 31.2357 },
> "userDeviceUtcTime": "2025-01-15T10:30:00Z",
> "requestFieldsValues": {
> "fullName": "Ahmed Mohamed",
> "nationalId": "29901234567890",
> "monthlyIncome": "5000"
> }
> }'

Save the returned id — this is the request ID required for all signing steps.


Step 5 — Admin approval

After creation, the request enters Pending Approval status. How approval works depends on your tenant’s configured approval type:

Approval types

TypeBehaviour
Manual (default)Each request requires explicit approval via portal or API
Pre-ApprovedRequest is created directly with status Approved — no review required
AutomatedRequest becomes Pending Approval, then your backend is called to approve or reject automatically

Automated approval

When automated approval is configured for a request type, Vlens calls your configured backend URL with the request ID. Your server should retrieve the request fields and call approve or reject:

$# Vlens calls your backend with:
$POST https://your-backend.com/approve
$Content-Type: application/json
${ "requestId": "REQUEST_ID" }
$
$# Your backend then calls:
$curl -X POST "https://api.vlenseg.com/api/BusinessRequest/ProcessAutomatedApproval/REQUEST_ID" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer ADMIN_TOKEN"

Approve or reject via API (Manual)

$curl -X POST "https://api.vlenseg.com/api/BusinessRequest/ApproveOrRejectRequestApi" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer ADMIN_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "id": "REQUEST_ID",
> "isApproved": true
> }'

Pass "isApproved": false to reject. Optional fields: requestFields (to override field values), skipPayment, contractSigners, DynamicSubTypeId.

Approve via portal

Navigate to Manage request traffic → select the contract → Actions → Approve/Reject.

Subscribe to the App.BusinessRequestStatusChange webhook in the portal to receive real-time status updates instead of polling.

Webhook payload example:

1{
2 "WebhookEvent": "App.BusinessRequestStatusChange",
3 "Data": {
4 "Id": "81f404b3-d7dc-4f08-b4fe-934853c86282",
5 "NewStatus": "Approved"
6 },
7 "CreationTimeUtc": "2025-01-15T10:35:00Z"
8}

Step 6 — Customer signing flow

Once approved, the user activates the contract via a location-checked OTP flow.

6a — Request OTP

$curl -X POST "https://api.vlenseg.com/api/BusinessRequest/CustomerSign/StepRequestOtp" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer USER_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "requestId": "REQUEST_ID",
> "transactionId": "NEW_UUID",
> "geoLocation": { "latitude": 30.0444, "longitude": 31.2357 },
> "userDeviceUtcTime": "2025-01-15T10:30:00Z",
> "smsProviders": 0
> }'

Generate a fresh UUID for transactionId before calling StepRequestOtp and pass the same value to all subsequent signing steps (StepReValidateLiveness, StepValidateOtp, StepValidatePayment). This is separate from the KYC transaction_id.

Location check: The system compares the submitted location against the user’s last active device location.

6b — Handle location change (if needed)

If the location check fails, call StepReValidateLiveness before retrying StepRequestOtp. Use the same transactionId you generated for the signing flow.

$curl -X POST "https://api.vlenseg.com/api/BusinessRequest/CustomerSign/StepReValidateLiveness" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer USER_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "requestId": "REQUEST_ID",
> "transactionId": "SIGNING_FLOW_TRANSACTION_ID",
> "geoLocation": { "latitude": 25.0444, "longitude": 35.2357 },
> "userDeviceUtcTime": "2025-01-15T10:30:00Z",
> "scanTransaction": {
> "image": "BASE64_FACE_IMAGE"
> }
> }'

6c — Validate OTP

$curl -X POST "https://api.vlenseg.com/api/BusinessRequest/CustomerSign/StepValidateOtp" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer USER_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "requestId": "REQUEST_ID",
> "transactionId": "SIGNING_FLOW_TRANSACTION_ID",
> "otpRequestId": "OTP_REQUEST_ID",
> "otpCode": "123456",
> "geoLocation": { "latitude": 30.0444, "longitude": 31.2357 },
> "userDeviceUtcTime": "2025-01-15T10:31:00Z"
> }'

6d — Validate payment and activate

$curl -X POST "https://api.vlenseg.com/api/BusinessRequest/CustomerSign/StepValidatePayment" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer USER_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{
> "requestId": "REQUEST_ID",
> "transactionId": "SIGNING_FLOW_TRANSACTION_ID",
> "geoLocation": { "latitude": 30.0444, "longitude": 31.2357 },
> "userDeviceUtcTime": "2025-01-15T10:32:00Z"
> }'

On success the contract moves to Customer Signed (requeststatus: 4).


Request statuses

ValueStatusDescription
1Pending ApprovalAwaiting admin review
2ApprovedAdmin approved — user can now sign
3RejectedRejected by admin
4Customer SignedUser completed the OTP signing flow
5Service Provider Partially SignedNot all designated signers have countersigned
6Service Provider SignedAll signers countersigned — fully executed
7CancelledCancelled before completion
8ExpiredContract expired before completion

Retrieve contracts

$# Most recent active request — full details including fields, documents, and status
$curl "https://api.vlenseg.com/api/BusinessRequest/Current" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer USER_TOKEN"
$
$# All requests — full details for each (heavier, use for dashboards)
$curl "https://api.vlenseg.com/api/BusinessRequest/CurrentList" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer USER_TOKEN"
$
$# All requests — IDs and statuses only (lightweight, good for list views)
$curl "https://api.vlenseg.com/api/BusinessRequest/CurrentListIds" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer USER_TOKEN"
$
$# Specific request by ID (user)
$curl "https://api.vlenseg.com/api/BusinessRequest/GetBusinessRequestById?Id=REQUEST_ID" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer USER_TOKEN"
$
$# Specific request by ID (admin — includes extra fields)
$curl "https://api.vlenseg.com/api/BusinessRequest/GetBusinessRequestByIdForAdmin?Id=REQUEST_ID" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer ADMIN_TOKEN"

Retrieve contract documents

$# Download signed contract as PDF (admin)
$curl "https://api.vlenseg.com/api/BusinessRequest/GetContractToPdf?Id=REQUEST_ID" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer ADMIN_TOKEN"
$
$# Download signed contract as PDF (customer)
$curl "https://api.vlenseg.com/api/BusinessRequest/GetCustomerContractToPdf?Id=REQUEST_ID" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer USER_TOKEN"
$
$# Get contract as HTML
$curl "https://api.vlenseg.com/api/BusinessRequest/GetContractHtml?contractId=REQUEST_ID" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer USER_TOKEN"

Service provider countersigning

Once the customer signs, a user with the BusinessRequestContractSigner role must countersign. This can be done:

  • Manually in the Vlens portal: navigate to Manage request traffic → select the contract → Actions → Sign Contract
  • Via API — get all Customer Signed request IDs, then sign each one:
$# Get all contracts waiting for provider countersignature
$curl "https://api.vlenseg.com/api/BusinessRequest/GetCustomerSignedRequestIds" \
> -H "ApiKey: YOUR_API_KEY" -H "Authorization: Bearer ADMIN_TOKEN"
$
$# Sign a specific contract
$curl -X POST "https://api.vlenseg.com/api/BusinessRequest/SignContractUsingSigningServer" \
> -H "ApiKey: YOUR_API_KEY" \
> -H "Authorization: Bearer ADMIN_TOKEN" \
> -H "Content-Type: application/json" \
> -d '{"id": "REQUEST_ID"}'
  • Automatically using the serviceprovider_sign_api tool, which polls for Customer Signed contracts at a configured interval

For auto-signing, configure the tool’s .env with VLENS_USERNAME, VLENS_PASSWORD, VLENS_TENANCY, VLENS_APIKEY, VLENS_BASEURL, RequestType, and SignEvery_X_Minutes.