Skip to main content

Authorisation

SDK

We provide a PHP SDK with an example integration to make it easier and faster to get ready to verify your customers' age:

VerifyMyAge Adult PHP SDK

Postman Workspace

We've created a Postman workspace specifically for our API, which contains example API calls that you can use to test and familiarise yourself with our API.

The workspace includes example API calls for each of our API endpoints, along with detailed descriptions of the request parameters and headers being used.

You can generate most of the client code to call our APIs using the Postman client code generator. With this feature, developers can select a range of programming languages, and generate the corresponding code with a few clicks.

Please note that while both the example API calls and the generated code can be a helpful starting point, it's important to thoroughly test your own API calls before deploying them in production. If you encounter any issues while using the example API calls, or if you have any questions about our API, please don't hesitate to reach out to our support team.

API Domain

Our API is designed to be used in two environments: a sandbox environment and a production environment.

The sandbox environment is intended for testing and development purposes, while the production environment is used for live data and real-world use cases.

To ensure the security and integrity of our API, we use separate API keys for each environment. This means that you will need to obtain different API keys for the sandbox and production environments, and should not use the same key for both.

DomainEnvironment
https://oauth.verifymyage.comproduction
https://sandbox.verifymyage.comsandbox

User Journey

Each country has different requirements for accessing adult content which are laid out by the regulator of that country.

A user is required to verify their age the first time they visit your site. They simply log in to their VerifyMyAge account for future sessions.

Integration Steps

There are 3 basic steps to generate a verification link for your user and receive updates on their verification status.

1. Your server performs a POST request to get their verification URL

2. Your server performs a POST request to exchange the code for an access_token

3. You can now receive status updates on their verification

You can confirm the user is age-verified by calling the user details endpoint using the access_token received.

Verification Flow

Generating the HMAC header

To improve the security of the communication between your implementation and the undefined API, we require you to generate a unique hexadecimal encoded SHA256 HMAC hash for each request, based on the input parameters.

The process of generating it depends on the language of your implementation.

If you use our SDK, the HMAC step will be abstracted,
and you don't need to do anything related to it.
Copy

Copied!

<?php

$api_key = YOUR_API_KEY;
$api_secret = YOUR_SECRET_KEY;
$hmac_signature = hash_hmac('sha256', $input, $api_secret);
$hmac_header_value = 'hmac ' + $api_key + ':' + $hmac_signature;

?>

Copy

Copied!

const crypto = require('crypto');
    
const secret = YOUR_SECRET_KEY
const apiKey = YOUR_API_KEY
const hmacSignature = crypto.createHmac('sha256', secret)
                    .update(YOUR_PAYLOAD_STRING)
                    .digest('hex');
const hmacHeaderValue = "hmac " + apiKey + ":" + hmacSignature;
Copy

Copied!

import hmac
import hashlib

def generateAuthHmacHeader(api_key, api_secret, request_body):
    sign_bytes = hmac.new(api_secret.encode('utf-8'), request_body.encode('utf-8'), hashlib.sha256).digest()
    sign_hex = sign_bytes.hex()
    authorization_header = f"hmac {api_key}:{sign_hex}"
    return authorization_header
Copy

Copied!

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func generateAuthHmacHeader(apiKey, apiSecret, requestBody []byte]) string {
    hmac := hmac.New(sha256.New, []byte(apiSecret))
    h.Write(requestBody)

    signBytes := h.Sum(nil)

    signHex := hex.EncodeToString(signBytes)
    authorizationHeader := fmt.Sprintf("hmac %s:%s", apiKey, signHex)
    return authorizationHeader
}
Copy

Copied!

User information encryption example

This example demonstrates how to encrypt user information sent in the user_info parameter.

<?php
    public function encrypt($text)
    {
        $secretHash         = substr(hash('sha256', $this->clientSecret, true), 0, 32);
        $iv                 = openssl_random_pseudo_bytes(16);
        $ciphertext         = openssl_encrypt($text, 'AES-256-CFB', $secretHash, OPENSSL_RAW_DATA, $iv);
        $ciphertext_base64  = base64_encode($iv . $ciphertext);    
        return $ciphertext_base64;
    }
?>
Copy

Copied!


function encryptText(keyStr, text) {
    const hash = crypto.createHash('sha256');
    hash.update(keyStr);
    const keyBytes = hash.digest();  
    const iv = crypto.randomBytes(16);
    const cipher = crypto.createCipheriv('aes-256-cfb', keyBytes, iv);
    let enc = [iv, cipher.update(text, 'utf8')];
    enc.push(cipher.final());
    return Buffer.concat(enc).toString('base64');
}
Copy

Copied!


    import hashlib, hmac, base64, os

    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
    from cryptography.hazmat.backends import default_backend
    def encrypt_aes(secret: str, data: str) -> str:
        key = hashlib.sha256(secret.encode()).digest()
        plain_text = data.encode()
        iv = os.urandom(16)
        cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
        encryptor = cipher.encryptor()
        cipher_text = encryptor.update(plain_text) + encryptor.finalize()
        iv_cipher_text = iv + cipher_text
        encrypted_data = base64.b64encode(iv_cipher_text).decode('utf-8')
        return encrypted_data
Copy

Copied!


        import (
            "crypto/aes"
            "crypto/cipher"
            "crypto/rand"
            "crypto/sha256"
            "encoding/base64"
        )
        func EncryptAES(secret, data string) (string, error) {
            keyBytes := sha256.Sum256([]byte(secret))
            block, err := aes.NewCipher(keyBytes[:])
            if err != nil {
                return "", err
            }
            plainText := []byte(data) iv := make([]byte, 16)
            _, err = rand.Read(iv)
            if err != nil {
                return "", err
            } 
            cfb := cipher.NewCFBEncrypter(block, iv)
            cipherText := make([]byte, len(plainText))
            cfb.XORKeyStream(cipherText, plainText)
            ivcipherText := append(iv, cipherText...)
            return base64.StdEncoding.EncodeToString(ivcipherText), nil
    }
Copy

Copied!

STEP 1: Get the URL to start the verification flow.

This will generate the URL to redirect your user to and the verification_id to start the verification flow.

Request Header

You have to sent the HMAC header in the request. HMAC example.

Request parameters

redirect_uri required

URL that the user will be redirected to after the age-verification flow.

country required

2-letter ISO country code. Available options: gb, de, fr, or us.

webhook optional

A webhook URL can be provided to receive real-time notifications when a user engages with the verification flow.

See the Webhook Request Example for more information.

method optional

ValueDescriptionGBDEFRUS
AgeEstimationEstimates an individual's age using a quick selfie video✔️✔️✔️⬜️
EmailEstimates an individual's minimum age using their email address⬜️⬜️⬜️⬜️
IDScanGovernment-issued ID✔️⬜️✔️⬜️
IDScanFaceMatchGovernment-issued ID verification combined with a face match for the holder✔️✔️⬜️✔️
CreditCardCredit card verification✔️⬜️✔️⬜️

business_settings_id optional

Id of bussiness configuration.

Important: This parameter sets the bespoke customisations we’ve made available to you.

external_user_id optional

UUID to identify an user.

verification_id optional

UUID to identify a verification.

otp_webhook optional

URL that will receive the ownership check result after the email confirmation process is completed. This parameter is required when run_otp is set to true..

user_info optional

Object containing the encrypted email address for an user.

Important: This field is required if you have “stealth” set to “true”.

{
  "user_info": {
    "email": "encrypted_email_address"
  }
}

The email address must be encrypted. Encryption Example.

stealth optional

A boolean query string is used to enable the stealth option. Defaults to “false”.

When you request us to operate in stealth mode, we will try to verify the user in the background. If the user is approved, we will return an approved status. If verification fails, we will provide a link to the standard verification process, which will offer the user additional verification options.

Important: Due to regulatory restrictions, you can only use our Email Address method in stealth if you're confirming ownership of emails during the sign-up process and as part of the email address update process on accounts on your site.

ValueDescription
trueWe will attempt to verify the user's email address in the background.
falseWe will not attempt to verify the user's email in the background.

run_otp optional

A boolean query string is used to determine whether OTP verification should be performed as part of the verification process. Defaults to 'false'.

ValueDescription
trueAn email containing a confirmation URL will be sent to the user's provided email address.
falseThe email-based ownership confirmation step will be skipped. Note that your account permissions may still override this option.

Error responses

CodeDescription
400
{"message": "Method must be one of the active methods. Take a look at the documentation","status_code": 400}
Copy

Copied!

400
{"message": "Country must be one of the active countries. Take a look at the documentation","status_code": 400}
Copy

Copied!

400
{"message": "Verification ID is not valid.","status_code": 400}
Copy

Copied!

400
{"message": "Business Settings ID is not valid.","status_code": 400}
Copy

Copied!

400
{"message": "Invalid email data","status_code": 400}
Copy

Copied!

400
{"message": "Redirect URL is not allowed","status_code": 400}
Copy

Copied!

400
{"message": "Webhook URL is not valid.","status_code": 400}
Copy

Copied!

401
{"message": "Invalid Credentials","status_code": 401}
Copy

Copied!

500
{"message": "Internal Server Error","status_code": 500}
Copy

Copied!

API Call
POST /v2/auth/start HTTP/1.1
Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-BODY
Content-Type: application/json

{
    "country": "gb",
    "method": "",
    "redirect_url": "https://your-domain.com/your-path",
    "business_settings_id": "",
    "external_user_id": "",
    "verification_id": "",
    "webhook": "https://your-callback-url.com/callback?args...",
    "otp_webhook": "https://your-otp-callback-url.com/callback"
}
Copy

Copied!

curl -X POST https://sandbox.verifymyage.com/v2/auth/start?stealth=false' 
--header 'Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-BODY' 
--header 'Content-Type: application/json' 
--data '{
            "country": "gb",
            "method": "",
            "redirect_url": "https://your-domain.com/your-path",
            "business_settings_id": "",
            "external_user_id": "",
            "verification_id": "",
            "webhook": "https://your-callback-url.io/callback?args..."
        }'
Copy

Copied!

<?php
    require(__DIR__ . "/vendor/autoload.php");

    use \VerifyMyAge\OAuthV2;
    use \VerifyMyAge\Countries;
    use \VerifyMyAge\Methods;

    $vma = new OAuthV2("your-key", "your_secret_key", "https://your-domain.com/your-path");
    $response = $vma->getStartVerificationUrl(Countries::UNITED_KINGDOM, Methods::ID_SCAN);
?>
Copy

Copied!

Response 200: (application/json)
{
  "start_verification_url": "https://our-environment.com/start/verification/{TOKEN}",
  "verification_id": "verification-id-generated",
  "verification_status": "started"
}
Copy

Copied!

Step 2: Exchange code for the access token.

This will allow you to receive status updates on the verification.

You must send the “Authorisation” header using the Basic authentication/authorisation format.

The value is generated by the base64 string of the concatenation of your API Key and API Secret separated by a colon (:).

Example in PHP:

[
    'Authorization' => 'Basic ' . base64_encode(
        $apiKey . ':' . $apiSecret
    )
]

Request parameters

code required

The code received as a query parameter to your redirect_uri in the previous step.

Error responses

CodeDescription
400
{"message": "invalid client credentials", "status_code": 400}
Copy

Copied!

401
{"message": "client id and client secret does not match","status_code": 401}
Copy

Copied!

401
{"message": "Unauthorized redirect_uri","status_code": 401}
Copy

Copied!

500
{"message": "Internal Server Error","status_code": 500}
Copy

Copied!

API Call
POST /oauth/token HTTP/1.1
Content-Type: application/json
Authorization: Basic {BASE64}

{
    "code": "CODE-RECEIVED-ON-THE-FIRST-STEP"
}
Copy

Copied!

curl -d '{"code": "CODE-RECEIVED-ON-THE-FIRST-STEP"}' \
    -H "Content-Type: application/json" \
    -H "Authorization: Basic {BASE64}" \
    -X POST https://sandbox.verifymyage.com/oauth/token
Copy

Copied!

<?php
require(__DIR__ . "/vendor/autoload.php");

use \VerifyMyAge\OAuth;
use \VerifyMyAge\Countries;

$accessToken = $vma->exchangeCodeByToken($_GET['code']);
Copy

Copied!

Exchange Code Flow

Response 200: (application/json)
{
  "access_token": "RANDOM-CODE"
}
Copy

Copied!

Step 3: Use the access token to receive the verification status.

You can get the status of the verification now.

Request parameters

access_token required

The token generated by VerifyMyAge in Step 2.

Response parameters

age_verified

Boolean that represents whether the user completed the process or not.

ValueDescription
trueThe user has completed the verification process successfully.
falseThe user has not completed the verification process successfully

id

Unique Identifier representing a verification.

threshold

The age threshold the user has to meet. It is a fixed value of 18.

Error responses

CodeDescription
400
{"message": "malformed token", "status_code": 400}
Copy

Copied!

500
{"message": "error trying to save verification","status_code": 500}
Copy

Copied!

500
{"message": "invalid scope to present","status_code": 500}
Copy

Copied!

500
{"message": "Internal Server Error","status_code": 500}
Copy

Copied!

API Call
GET /users/me?access_token={TOKEN} HTTP/1.1

Copy

Copied!

curl https://sandbox.verifymyage.com/users/me?access_token={TOKEN}
Copy

Copied!

<?php
require(__DIR__ . "/vendor/autoload.php");

use \VerifyMyAge\OAuth;
use \VerifyMyAge\Countries;

$user = $vma->user($accessToken);
Copy

Copied!

Response 200: (application/json)
{
  "age_verified": true,
  "id": "ABC-12345-DEF-64321",
  "threshold": 18
}
Copy

Copied!

Retrieve Verification Status

This endpoint retrieve the verification status

Request Header

You have to sent the HMAC header in the request. HMAC example.

Response parameters

verification_id

Unique Identifier representing a verification.

verification_status

It represents the current status of the verification flow. It can be one of the following:

ValueDescription
startedThe user has not started the verification process.
pendingThe user has started but has not finished the verification process.
approvedThe user has completed the verification process successfully.
failedThe user has not completed the verification process successfully.
expired5 days have elapsed since the verification link was generated, and it has expired.

Error responses

CodeDescription
401
{"message": "Invalid Credentials", "status_code": 401}
Copy

Copied!

404
{"message": "verification not found!", "status_code": 404}
Copy

Copied!

API Call
GET /v2/verification/{ID}/status HTTP/1.1
Content-Type: application/json
Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-URI
Copy

Copied!

curl -X GET https://sandbox.verifymyage.com/v2/verification/{ID}/status
--header 'Content-Type: application/json'
--header 'Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-URI
Copy

Copied!

Response 200: (application/json)
{
  "verification_id": "ABC-12345-DEF-64321",
  "verification_status": "approved"
}
Copy

Copied!

Allowed Redirect URLs

URLs that are allowed to be redirected to after a successful verification.

Get allowed URLs

Retrieve a list of allowed redirect URLs.

Request Header

You have to sent the HMAC header in the request. HMAC example.

Response parameters

body

The list of Allowed Redirect URLs.

Error responses

CodeDescription
401
{"message": "invalid authentication", "status_code": 401}
Copy

Copied!

500
{"message": "Internal Server Error","status_code": 500}
Copy

Copied!

API Call
GET /v1/business/allowed-redirects HTTP/1.1
Content-Type: application/json
Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-URI

Copy

Copied!

curl https://sandbox.verifymyage.com/v1/business/allowed-redirects
    --header 'Content-Type: application/json'
    --header 'Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-URI'
    
Copy

Copied!

Response 200: (application/json)
{
  "body": [
    "https://your-website.com/redirect-1",
    "https://your-website.com/redirect-2"
  ]
}
Copy

Copied!

Add Allowed URLs

Add one or more allowed redirect URLs.

Request Header

You have to sent the HMAC header in the request. HMAC example.

Request parameters

body required

An array containing the allowed redirect URLs.

Error responses

CodeDescription
401
{"message": "invalid authentication", "status_code": 401}
Copy

Copied!

500
{"message": "Internal Server Error","status_code": 500}
Copy

Copied!

API Call
PATCH /v1/business/allowed-redirects HTTP/1.1
Content-Type: application/json
Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-BODY
[
    "https://your-website.com/redirect-1"
]

Copy

Copied!

curl -X PATCH https://sandbox.verifymyage.com/v1/business/allowed-redirects
    --header 'Content-Type: application/json'
    --header 'Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-BODY'
    --data '[
    "https://your-website.com/redirect-1"
]'
    
Copy

Copied!

Response 204

Replace All Allowed URLs

Replace any existing allowed redirect URLs with the provided list.

Request Header

You have to sent the HMAC header in the request. HMAC example.

Request parameters

body required

An array containing the allowed redirect URLs.

Error responses

CodeDescription
401
{"message": "invalid authentication", "status_code": 401}
Copy

Copied!

500
{"message": "Internal Server Error","status_code": 500}
Copy

Copied!

API Call
PUT /v1/business/allowed-redirects HTTP/1.1
Content-Type: application/json
Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-BODY
[
    "https://your-website.com/new-redirect-url"
]

Copy

Copied!

curl -X PUT https://sandbox.verifymyage.com/v1/business/allowed-redirects
    --header 'Content-Type: application/json'
    --header 'hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-BODY'
    --data '[
    "https://your-website.com/new-redirect-url"
]'
    
Copy

Copied!

Response 204

Delete Allowed URLs

Remove one or more allowed redirect URLs.

Request Header

You have to sent the HMAC header in the request. HMAC example.

Request parameters

body required

An array containing the allowed redirect URLs.

Error responses

CodeDescription
401
{"message": "invalid authentication", "status_code": 401}
Copy

Copied!

500
{"message": "Internal Server Error","status_code": 500}
Copy

Copied!

API Call
DELETE /v1/business/allowed-redirects HTTP/1.1
Content-Type: application/json
Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-BODY
[
    "https://your-website.com/redirect-1",
    "https://your-website.com/redirect-2"
]

Copy

Copied!

curl -X DELETE https://sandbox.verifymyage.com/v1/business/allowed-redirects
    --header 'Content-Type: application/json'
    --header 'Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-WITH-REQUEST-BODY'
    --data '[
    "https://your-website.com/redirect-1",
    "https://your-website.com/redirect-2"
]'
    
Copy

Copied!

Response 204

Webhook Notifications

We will send a notification to your specified callback URL when a user initiates or interacts with our verification.

  1. Request Method: Your webhook endpoint must be configured to handle POST requests.
  2. Headers:
  • We will send an Authorization header with each webhook request.
  • Format: Authorization: hmac YOUR-API-KEY:GENERATED-HMAC
  • The HMAC signature is generated using the secret key provided in your business configuration.
  1. Request Body: The webhook payload will be a JSON object containing relevant verification data.
  2. To ensure the authenticity of webhook requests, always verify the HMAC signature in the Authorization header.
  3. Use HTTPS for your webhook URL to encrypt data in transit.

Request parameters

verification_id required

UUID that identifies an user verification.

engaged required

Boolean to indicate if the user has engaged with the verification flow.

Webhook Call
POST /your/webhook/path HTTP/1.1
Host: https://your-domain.com
Content-Type: application/json
Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-SIGNATURE

{
    "verification_id": "uuid-1234-5678-uuid",
    "engaged": "true",
}

Copy

Copied!

curl -X POST https://your-domain.com/
        --H 'Authorization: hmac YOUR-API-KEY:GENERATE-HMAC-SIGNATURE'\
        --H 'Content-Type: application/json'
        --data-raw '{
            "verification_id": "uuid-1234-5678-uuid",
            "engaged": "true",
        }'
    
Copy

Copied!

Demo

You can try a demo of this integration at:

https://demo.verifymyage.com/