Card KYC

Verify a customer who will get a card, with a liveness check, through a hosted page or the API.

Card KYC verifies a customer who will get a card. The hosted page captures a liveness check in the same step, and card activation needs that check. For general customer verification, use KYC instead. See Which KYC flow to use.

Never call Start KYC verification for a customer whose KYC status is anything other than not_started. For a customer who is pending, failed or already completed, the call starts the review again and sets their KYC to pending. A completed customer loses that status until the new review finishes. Check the customer's kyc basic status with Get a customer first.

Required documents

For individual customers, upload a selfie and one of the following government-issued IDs:

  • Passport
  • National ID card
  • Driver's license

For business customers, upload one company document.

You can submit documents through a hosted page or through the API.

Gravv records the outcome on the customer. Get a customer returns kyc, and the status of the basic entry becomes completed or failed. Gravv also reports the outcome with a customer.kyc.status.completed or customer.kyc.status.failed webhook. Gravv sends no pending event for card KYC today, so read the customer to see that a review has started.

Submit through a hosted page

Use the Start KYC verification endpoint to send the customer to a hosted page, where they upload their ID documents and complete a liveness check. Send the customer's customer_id:

curl --request POST \
     --url https://api.gravv.xyz/v1/risk/start-kyc \
     --header 'Api-Key: <API_KEY>' \
     --header 'Idempotency-Key: 5a1e7c2d-3b4f-4d6e-8a9b-0c1d2e3f4a01' \
     --header 'accept: application/json' \
     --header 'content-type: application/json' \
     --data '
{
  "customer_id": "c239895a-0436-4287-a37a-c2664f68d187"
}
'

The response includes the verification status and a web_url link. Share this link with the customer so they can upload their ID documents. The link is valid for one hour:

{
  "data": {
    "applicant_id": "66f0c2a1b3c4d5e6f7a8b9c0",
    "external_user_id": "c239895a-0436-4287-a37a-c2664f68d187",
    "kyc_level": "basic",
    "review_status": "pending",
    "status": "success",
    "web_url": "https://<verification-host>/websdk/p/…"
  },
  "error": null
}

Every error from this endpoint is HTTP 400 with a message in error.

Submit through the API

To submit IDs through the API, encode them in Base64 and send them with the Upload document and Initiate server-to-server verification endpoints. Verification through the API records no liveness check, so card activation then returns a liveness link for the cardholder.

Upload documents

First, upload each required document with the Upload document endpoint. Encode each document in Base64 and send it as the value of the content field. Upload the back of a two-sided ID in a second request with idDocSubType BACK_SIDE.

curl --request POST \
     --url https://api.gravv.xyz/v1/risk/upload-document \
     --header 'Api-Key: <API_KEY>' \
     --header 'Idempotency-Key: 5a1e7c2d-3b4f-4d6e-8a9b-0c1d2e3f4a02' \
     --header 'content-type: application/json' \
     --data '
{
  "customer_id": "aabe0a33-6716-42e2-bbca-7abf1a8bd91c",
  "metadata": {
    "idDocType": "passport",
    "idDocSubType": "FRONT_SIDE",
    "country": "USA",
    "firstName": "John",
    "lastName": "Doe",
    "number": "P123456789",
    "issuedDate": "2020-01-15",
    "validUntil": "2030-01-15",
    "dob": "1990-05-20",
    "placeOfBirth": "New York, NY"
  },
  "content": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAY...",
  "filename": "passport_front.jpg"
}
'

A successful upload returns status success:

{
  "data": {
    "country": "USA",
    "customer_id": "aabe0a33-6716-42e2-bbca-7abf1a8bd91c",
    "document_sub_type": "FRONT_SIDE",
    "document_type": "PASSPORT",
    "message": "Document saved. Use /initiate-kyc to submit for verification.",
    "status": "success"
  },
  "error": null
}

The message names /initiate-kyc, but the next call is Initiate server-to-server verification.

A request that fails validation also returns HTTP 200, with data.status error. Treat an upload as saved only when data.status is success:

{
  "data": {
    "error_code": "INVALID_REQUEST",
    "message": "The provided data format is invalid. Please check your request and try again.",
    "status": "error"
  },
  "error": null
}

Each document upload takes these metadata fields:

Field Required for Description
idDocType All documents Document type, in lower case: passport, id_card, drivers_license, selfie, or for a business company_doc
country All documents Issuing country as a 3-letter ISO code, such as USA, GBR, NGA. Required for a selfie too
idDocSubType ID documents Document side: FRONT_SIDE or BACK_SIDE
number ID documents (optional) Document identification number
issuedDate ID documents (optional) Issue date in YYYY-MM-DD format
validUntil ID documents (optional) Expiration date in YYYY-MM-DD format
dob Optional Date of birth in YYYY-MM-DD format
firstName Optional First name on document
lastName Optional Last name on document
middleName Optional Middle name on document
placeOfBirth Optional Place of birth as shown on document

Each upload must also meet these rules:

  • filename must end in .jpg, .jpeg, .png or .pdf, and the content must be a JPEG, PNG or PDF. A selfie must be an image.
  • Keep each file under 5 MB. In sandbox, keep each file under 500 KB. Sandbox refuses requests from about 600 KB with a 413.
  • Gravv does not deduplicate a request body over 1 MiB under its Idempotency-Key. A retried upload of a file over about 770 KB therefore runs again instead of returning the first response. The retry replaces the earlier file of the same type and side.

Initiate verification

After you upload all required documents, call the Initiate server-to-server verification endpoint:

curl --request POST \
     --url https://api.gravv.xyz/v1/risk/start-kyc-s2s \
     --header 'Api-Key: <API_KEY>' \
     --header 'Idempotency-Key: 5a1e7c2d-3b4f-4d6e-8a9b-0c1d2e3f4a03' \
     --header 'content-type: application/json' \
     --data '
{
  "customer_id": "aabe0a33-6716-42e2-bbca-7abf1a8bd91c"
}
'

The endpoint returns HTTP 200 with one of these outcomes. Branch on data.status and data.code:

  • Submitted. Gravv sent all documents and started verification:
{
  "data": {
    "applicant_id": "507f1f77bcf86cd799439011",
    "message": "KYC verification initiated. Status will be updated via webhook.",
    "status": "pending"
  },
  "error": null
}

Wait for the outcome on the customer record or in a webhook.

  • Missing documents. You have not uploaded all the required documents yet:
{
  "data": {
    "code": "MISSING_DOCUMENTS",
    "details": {
      "missing_documents": [
        "SELFIE"
      ]
    },
    "message": "Required documents have not been uploaded for this customer"
  },
  "error": null
}

Upload the documents listed in details.missing_documents and call the endpoint again with a new Idempotency-Key. A customer whose KYC is already completed gets data.code KYC_ALREADY_COMPLETED instead.

  • Partial success. Gravv could not send some documents. The response includes a web_url where the customer can finish verification:
{
  "data": {
    "applicant_id": "507f1f77bcf86cd799439011",
    "failed_documents": [
      {
        "doc_type": "SELFIE",
        "reason": "Upload failed"
      }
    ],
    "message": "Upload failed",
    "status": "partial_success",
    "web_url": "https://<verification-host>/websdk/p/…"
  },
  "error": null
}

Other errors are HTTP 400 with a message in error.

Was this page helpful?