NAV Navbar
Ruby Python Javascript

Overview

Welcome to Persona! Here you’ll find comprehensive information for integrating with Persona and our API endpoints. We’ve tried to make this documentation user-friendly and example-filled.

If you’re planning to use our API in Production, take a look at our Privacy Policy. The fastest way to get your integration up and running is to setup an Embedded flow.

We're always happy to help with code or other questions you might have! Search our documentation, contact support, or connect with our sales team.

Terminology

Our core model is an Inquiry. Each inquiry contains all interactions with a single individual. You can customize a Template to specify which verifications any individual must pass in the inquiry. Whenever integrating with Persona, you specify a template to use for that flow.

As the individual progresses through the Persona Flow, they create multiple Verifications as they submit information for verification (e.g. form entry, driver license, or selfie). Once an individual has completed an inquiry, we'll search for information on that individual and generate the necessary Reports.

Verifications contribute to how likely someone is who they say they are, while reports describe them assuming they are that person.

Products

Embedded flow The embedded flow is a drop-in module that enables you to seamlessly verify individuals within your web page. It allows individuals to easily verify themselves without leaving their current experience.
Hosted flow The hosted flow is a full window experience that provides a secure, elegant inquiry flow. It is hosted by Persona but its branding and theming can be fully customized. Its an optimized flow that requires no engineering effort to enable you to begin verifying individuals now.
WebView flow The WebView flow is a native module that seamlessly verifies individuals inside your iOS/Android application.

Browser Support

Modern web browsers listed are fully supported.

Fully supported Fully supported
Fully supported 11+ supported

Mobile Support

Modern mobile browsers are supported, including most iPhone and Android devices.

See WebView flow for an example iOS/Android application using Persona.

Security

Security is at the core of our culture and we have operated from a security-first mentality from day one.

Persona's security philosophy follows three principles:

For more information about our security measures, head to our Security Statement or contact us.

Sandbox Environment

There are two runtime environments for the Persona flow: sandbox and production. The sandbox flow allows you to integrate before you go live without affecting your overall usage charge.

Integrate with Persona using the sandbox environment and then move to the production environment once you're confident with your integration.

Getting Started

Setting up Persona for the first time? Here are a couple quickstart guides for you:

Integration

Embedded Flow

With the Embedded Flow, user information necessary for verification is securely collected from your website running in the user’s browser. When a user launches Persona, an inquiryId unique to the user's session is generated. This inquiryId allows you to gain insights into a user’s verification session.

Quickstart: Embedded Flow

Persona can be integrated with the following code snippet. Remember to replace templateId with your your organization's templateId, which can be found in your dashboard.

A simple HTML Integration

<script src="https://cdn.withpersona.com/assets/v2/persona.js"></script>

<script>
  const client = new Persona.Client({
    templateId: "tmpl_WMmFNiixxX1doLfj5KDi6dQq",
    onLoad: () => client.open(),
    onComplete: meta => {
      // Inquiry completed. Optionally tell your server about it.
      console.log(`Sending finished inquiry ${meta.inquiryId} to backend`);
      fetch(`/server-handler?inquiry-id=${meta.inquiryId}`);
    }
  });
</script>

That's it! You're done.

Parameters

The Persona client accepts the following parameters.

templateId
required
An id corresponding to a template.
inquiryId
optional
Specify an inquiryId to resume an existing inquiry. The sessionToken parameter is required if the inquiry's status is pending.
sessionToken
optional
When resuming an inquiry with a `pending` status, you must also generate a session token from the server-side API.
environment
optional
The Persona API environment on which to create inquiries. For sandbox and production, use `sandbox` and `production` respectively.
referenceId
optional
You can generate and provide a unique id which we will associate with the inquiry. The identifier can be used to monitor user progress in newly created inquiries.
language
optional
Specify a supported language to localize the flow. Language will be inferred from browser settings by default.
prefill
optional
Provide an object to prefill form inputs in the flow. Each attribute in the object is optional.
onStart
required
A function that is called when a user starts the flow. The function should expect a single argument, a string inquiry id that references the in-progress inquiry.
onSuccess
required
A function that is called when a user has successfully verified their identity. It is passed a metadata object.
onLoad
optional
A function that is called when the Persona module has finished loading. Calls to open() prior to the onLoad callback will still open the flow, and a loading spinner will be shown.
onExit
optional
A function that is called when a user has specifically exited the flow. The function should expect two arguments, a nullable error object and a metadata object.
onEvent
optional
A function that is called when a user reaches certain points in the inquiry flow. The function should expect two arguments, an eventName string and a metadata object.

Prefill parameters object

{
  "name-first": "Jane",
  "name-last": "Doe",
  "birthdate": "1957-05-29",
  "address-street-1": "132 Second St.",
  "address-city": "San Francisco",
  "address-subdivision": "California",
  "address-postal-code": "93441",
  "country-code": "US"
}

To retrieve information about inquiries, see Retrieving inquiries.

Callbacks

onExit

The onExit callback is called when a user exits the inquiry flow. It takes two arguments, a nullable error object and a metadata object.

onEvent

Example metadata, eventName = verification-select

{
  "type": "verification/selfie"
}

Example metadata, eventName = verification-change

{
  "id": "ver_rMkwDnyoZy6b1jDSofjUtL5z",
  "status": "submitted"
}

The onEvent callback is called at certain points in the Persona flow. It takes two arguments, an eventName string and a metadata object.

eventName
String
A string representing the event that has just occurred in the Persona flow.
metadata
Object
An object containing information about the event.

Event names you may receive include:

name description
start The user has begun progressing through the flow.
country-select The user confirmed the country of their Id.
verification-select The user selected a verification to begin.
verification-change The status of an in-progress verification changed.
document-upload The user uploaded a document.
one-time-link-sent The user sent a one time link to their mobile device.
one-time-link-start The user resumed their flow on their mobile device using a one-time link.
one-time-link-exit The user returned to their initial device after sending a link to their mobile device.
success The user has been successfully verified.
complete The user is exiting the flow after verifying their identity.

The metadata parameter is always present, though some values may be null. Please note that new event names, metadata keys, or view names may be added without notice.

Opening and Closing

Opening

client.open() will display the intro page to your user, starting the Persona flow. Once open is called, you will begin receiving events via the onEvent callback.

Closing

client.exit() allows you to programmatically close the Persona flow. Calling exit will trigger the onExit callback. It takes a single, optional argument, a configuration object.

The configuration options are:

force
boolean
If true, Persona will exit immediately. If false, or the option is not provided, an exit confirmation screen may be presented to the user.

Allowed iframe domains

The embedded flow can only be embedded on domains pre-configured for each template. Restrict your list of domains to prevent your templates from being used by third-parties. To modify or update allowed iframe domains, contact us.

Hosted Flow

The hosted flow allows users to verify their identity in a fully optimized flow. Persona hosts and manages the entire flow. Whenever a user enters the flow, we generate an inquiry id that is unique to the user’ journey. This inquiry id allows you to monitor a user’s progress as they verify their identity.

Quickstart: Hosted Flow

Getting users to your Hosted Flow is as simple as getting them to your Hosted Flow link, which can be found in the Development tab of your dashboard.

Directing to the flow

Direct users to the hosted flow link

redirect_to 'https://withpersona.com/verify?template-id=tmpl_WMmFNiixxX1doLfj5KDi6dQq&redirect-uri=https://withpersona.com&reference-id=<your-unique-id>'
redirect('https://withpersona.com/verify?template-id=tmpl_WMmFNiixxX1doLfj5KDi6dQq&redirect-uri=https://withpersona.com&reference-id=<your-unique-id>')
window.location.assign(
  "https://withpersona.com/verify?template-id=tmpl_WMmFNiixxX1doLfj5KDi6dQq&redirect-uri=https://withpersona.com&reference-id=<your-unique-id>"
);

To start the hosted flow, link a user to /verify on Persona with the appropriate GET parameters. To test the hosted flow in the sandbox, use the sandbox host domain.

template-id
required
This id corresponds to a pre-set configuration and determines how the flow is customized.
environment
optional
The Persona API environment on which to create inquiries. For sandbox and production, use `sandbox` and `production` respectively.
redirect-uri
optional
When the user successfully verifies their identity, we redirect back to this URL. The callback should expect a GET parameter named `inquiry-id` that references the completed inquiry. If no redirect-uri is specified, then the success page shown to users will not have a continue button.
inquiry-id
optional
Specify an inquiry id to resume an existing inquiry. If the inquiry has a `pending` status, then a sessionToken from the server-side API is required.
session-token
optional
When resuming an inquiry with a `pending` status, you must also generate a session token from the server-side API.
reference-id
optional
You can generate and provide a unique id which we will associate with the inquiry. The identifier can be used to monitor user progress in newly created inquiries.
language
optional
Specify a supported language to localize the flow. Language will be inferred from browser settings by default.
prefill
optional
Provide an object to prefill form inputs in the flow. Each attribute in the object is optional.

Prefill parameters

prefill['name-first']=Jane&prefill['name-last']=Doe&prefill['birthdate']=1957-05-29&
prefill['address-street-1']=132 Second St.&prefill['address-city']=San Francisco&
prefill['address-subdivision']=California&prefill['address-postal-code']=93441&
prefill['country-code']=US

Success callback

Set the redirect-uri GET parameter to your URL that will process responses from Persona. It should expect a inquiry-id query parameter.

It should optionally expect a reference-id GET parameter if you provided one when starting the flow. For more detail on retrieving inquiries with the inquiry-id, see Retrieving inquiries.

redirect-uri
required
When the user successfully verifies their identity, we redirect the user back to this URL. The callback should expect a GET parameter named `inquiry-id` that references the completed inquiry.
reference-id
optional
You can generate and provide a unique id which we will associate with the inquiry. The identifier can be used to monitor user progress in newly created inquiries.

Allowed redirect urls

The hosted flow can only redirect to URLs pre-configured for each template. Restrict your list of domains to prevent your templates from being used by third-parties. To modify or update allowed redirect domains, contact us.

WebView Flow

The WebView flow allows users to verify their identity in a fully optimized mobile flow. Persona hosts and manages the entire flow. Whenever a user enters the flow, we generate an inquiry id that is unique to the user’ journey. This inquiry id allows you to monitor a user’s progress as they verify their identity.

Quickstart WebView Flow

To help you integrate Persona with your mobile application, we've created example applications that showcase a clean integration for both iOS and Android. This approach seamlessly loads the Hosted Flow in a web view.

Example applications

An example Android application using Persona in a WebView is available at persona-id/persona-android-WebView

An example iOS application using Persona in a WebView is available at persona-id/persona-ios-WebView

Loading the WebView

Create a WebView and load /verify on Persona with the appropriate GET parameters. To test the WebView flow in the sandbox, use the sandbox host domain.

is-WebView
required
Should be set to true.
template-id
required
This id corresponds to a pre-set configuration and determines how the flow is customized.
environment
optional
The Persona API environment on which to create inquiries. For sandbox and production, use `sandbox` and `production` respectively.
reference-id
optional
You can generate and provide a unique id which we will associate with the inquiry. The identifier can be used to monitor user progress in newly created inquiries.

iOS Swift integration

  class PersonaViewController: UIViewController, WKNavigationDelegate {
      let WebView = WKWebView()

      override func viewDidLoad() {
      // load the Persona url
      let url = URL(string: generatePersonaInitializationURL())
      let request = URLRequest(url: url!)

              // ...

              self.view.addSubview(WebView)
              WebView.load(request)

      }

      // generatePersonaInitializationURL :: create the Persona url with query parameters
      func generatePersonaInitializationURL() -> String {
      let config = [
      "template-id": "tmpl_PDGZKPEASz266wmRcPbuwjPP",
      "redirect-uri": "https://personademo.com",
      "is-WebView": "true",
      "environment": "sandbox",
      ]

              // Build a dictionary with the Persona configuration options
              // See the Persona docs (http://documentation.withpersona.com) for full documentation.
              var components = URLComponents()
              components.scheme = "https"
              components.host = "withpersona.com"
              components.path = "/verify"
              components.queryItems = config.map { URLQueryItem(name: $0, value: $1) }
              return components.string!

      }

      // ...

Camera permissions

Android camera configuration

On Android, verifications that use the camera require you to override permission granting and implement a file chooser. The manifest file must include camera and audio permissions.

Android code example

webView.setWebChromeClient(new WebChromeClient() {
  @Override
  public void onPermissionRequest(final PermissionRequest request) {
    Log.d(TAG, "onPermissionRequest");
    runOnUiThread(new Runnable() {
      @TargetApi(Build.VERSION_CODES.LOLLIPOP)
      @Override
      public void run() {
        if(request.getOrigin().toString().equals("https://personacallback/")) {
          request.grant(request.getResources());
        } else {
          request.deny();
        }
      }
    });
  }

  @Override
  public boolean onShowFileChooser(
      WebView webView, ValueCallback<Uri[]> newFilePathCallback,
      FileChooserParams fileChooserParams) {
    // Implement a file chooser or camera here
  }
});

iOS camera configuration

On iOS, the Info.plist file must include camera permissions.

Success URL

The redirect URI is loaded when the user successfully verifies their identity. It is configured in the template. Catch the redirect URI in shouldOverrideUrlLoading.

Android code example

public boolean shouldOverrideUrlLoading(WebView view, String url) {
  Uri parsedUri = Uri.parse(url);
  if (parsedUri.getAuthority().equals("personacallback")) {
    HashMap<String, String> personaData = parsePersonaUriData(parsedUri);

    Log.d("Inquiry Id: ", personaData.get("inquiry-id"));
    Log.d("Reference Id: ", personaData.get("reference-id"));
  }
}

iOS Swift code example

func webView(_ webView: WKWebView,
            decidePolicyFor navigationAction: WKNavigationAction,
            decisionHandler: @escaping ((WKNavigationActionPolicy) -> Void)) {

  let actionType = navigationAction.request.url?.host;
  let queryParams = getUrlParams(url: navigationAction.request.url!)

  if (actionType == "personacallback") {
      // User succeeded verification
      print("Inquiry Id: \(queryParams["inquiry-id"]!)");
      print("Reference Id: \(queryParams["reference-id"]!)");

      // Do nothing
      // You will likely want to transition the view at this point.
      decisionHandler(.cancel)
  } else {
      // Unknown case - do not override URL loading
      decisionHandler(.allow)
  }
}

Webhooks

Use webhooks to be notified about events that happen in an inquiry by setting up webhooks in your dashboard. Any time an event happens (i.e. an inquiry changes statuses to Approved), Persona can send a webhook events to notify your application.

Setup new webhooks

Set up a new webhook in your dashboard. Only enabled events in the specified environment will be sent to your application. You can use a tool like ngrok to test webhooks locally in your sandbox environment.

Responding to events

Webhook event request

{
  "data": {
    "type": "event",
    "id": "evt_XGuYWp7WuDzNxie5z16s7sGJ",
    "attributes": {
      "name": "inquiry.approved",
      "payload": {
        "data": {
          "type": "inquiry"
          "id": "inq_2CVZ4HyVg7qaboXz2PUHknAn",
          "attributes": {
            "status": "approved",
            "reference-id": null,
            "created-at": "2019-09-09T22:40:56.000Z",
            "completed-at": "2019-09-09T22:44:51.000Z",
            "expired-at": null
          },
          "relationships": {
            "reports": {
              "data": []
            },
            "template": {
              "data": {
                "id": "blu_biqYXr3aNfHuLeXUdJUNFNET",
                "type": "template"
              }
            },
            "verifications": {
              "data": [
                {
                  "id": "ver_KnqQRXmxmtquRE65CHTzymhR",
                  "type": "verification/driver-license"
                },
                {
                  "id": "ver_2aguhcwq66zcmqipmrqjxw68",
                  "type": "verification/selfie"
                }
              ]
            }
          },
        },
        "included": [
          {
            "type": "template"
            "id": "blu_biqYXr3aNfHuLeXUdJUNFNET",
            "attributes": {
              "name": "Acme #1"
            },
          },
          {
            "type": "verification/driver-license"
            "id": "ver_KnqQRXmxmtquRE65CHTzymhR",
            "attributes": {
              "status": "passed",
              "front-photo-url": "...",
              "created-at": "2019-09-09T22:41:29.000Z",
              "completed-at": "2019-09-09T22:41:40.000Z",

              ...
            },
          },
          {
            "type": "verification/selfie"
            "id": "ver_2aguhcwq66zcmqipmrqjxw68",
            "attributes": {
              "status": "passed",
              "center-photo-url": "...",
              "left-photo-url": "...",
              "right-photo-url": "...",
              "created-at": "2019-09-09T22:42:43.000Z",
              "completed-at": "2019-09-09T22:42:46.000Z",

              ...
            },
          }
        ]
      }
      "created-at": "2019-09-09T22:46:27.598Z",
    }
  }
}

Webhook events are sent to your application according to the JSONAPI specification. Affected and related objects are included in the payload attribute. Objects within the payload use the same schema as their API resource endpoints.

Your endpoint must return an HTTP status code of 200. If Persona does not receive a status code of 200 from your application, it will retry up to 5 more times until it does.

Checking signatures


t, v1 = request.headers['Persona-Signature'].split(',').map { |value| value.split('=').second }
computed_digest = OpenSSL::HMAC.hexdigest('SHA256', secret, "#{now}.#{json}")

if v1 == computed_digest
  # Handle verified webhook event
end
t, v1 = [value.split('=').second for value in request.headers['Persona-Signature'].split(',')]
computed_digest = hmac.new(secret.encode(), (t + '.' + request.body).encode(), 'sha256').hexdigest()

if hmac.compare_digest(v1, computed_digest):
  # Handle verified webhook event

Verify that an incoming webhook event is from Persona with HMAC. Every webhook event contains a Persona-Signature header with a computed digest. Compare this value with your own digest computed from the request body and your webhook secret to ensure that the webhook event is safe to process. You can find your webhook secret event in the Webhook configuration section of your dashboard.

Preventing replay attacks

A replay attack is when attacker captures a valid webhook event with its signature, and re-transmits them. To mitigate such attacks, the Persona-Signature header contains a timestamp of when the event was sent. We recommend determining a tolerance window of 5 minutes or less and ignoring requests outside the window.

API Reference

We support operations on inquiries, reports, and verifications. Please contact us to get access to the detailed API.

API keys

To gain access to the Persona API, please contact our sales team.

We'll provide you with a sandbox api_key for evaluation of the API. Once you’ve completed the signup process and acknowledged our terms, we’ll provide a production api_key.

Your API keys carry many privileges, so be sure to keep them secure! Do not share your secret API keys in publicly accessible areas such as GitHub, client-side code, and so forth.

Authentication

Authentication is performed via HTTP Bearer Authentication. Provide your API key as the bearer token value. All API requests must be made over HTTPS. Calls made over plain HTTP will fail. API requests without authentication will also fail.

Protocol (REST)

The Persona API uses GETPOST, and DELETE requests to communicate and HTTP response codes to indicate status and errors. All responses come in standard JSON. The Persona API is served over HTTPS TLS v1.1+ to ensure data privacy; HTTP and HTTPS with TLS versions below 1.1 are not supported. All requests must include a Content-Type of application/json and the body must be valid JSON.

JSON Request/Response

{
  "inquiry-id": "..."
}

Member names in requests and responses are expected to follow kebab-case.

Idempotent Requests

Using Idempotency-Key

require 'http'

response = HTTP.
  headers(
    'Authorization': "Bearer #{api_key}",
    'Idempotency-Key': idempotency_key,
  ).
  post('https://withpersona.com/api/v1/inquiries/inq_4eC39HqLyjWDarjtT1zdp7dc/approve')

inquiry = JSON.parse(response.body)
import requests

response = requests.get(
  'https://withpersona.com/api/v1/inquiries/inq_4eC39HqLyjWDarjtT1zdp7dc/approve',
  headers: {
    'Authorization': 'Bearer {}'.format(api_key),
    'Idempotency-Key': idempotency_key,
  },
)

inquiry = response.json()
const request = require("request");

request(
  {
    json: true,
    url: `https://withpersona.com/api/v1/inquiries/inq_4eC39HqLyjWDarjtT1zdp7dc/approve`,
    headers: {
      Authorization: `Bearer ${apiKey}`,
      "Idempotency-Key": idempotency_key
    }
  },
  (err, res, body) => {
    inquiry = res;
  }
);

The Persona API supports idempotency for safely retrying requests without accidentally performing the same operation twice. This is useful when an API call is disrupted in transit and you do not receive a response. For example, if a request to create an inquiry does not respond due to a network connection error, you can retry the request with the same idempotency key to guarantee that no more than one inquiry is created.

To perform an idempotent request, provide an additional Idempotency-Key: <key> header to the request.

Persona's idempotency works by saving the resulting status code and body of the first request made for any given idempotency key, regardless of whether it succeeded or failed. Subsequent requests with the same key return the same result, including 500 errors.

All POST requests accept idempotency keys. Sending idempotency keys in GET and DELETE requests has no effect and should be avoided, as these requests are idempotent by definition.

Inquiries Resource

GET /api/v1/inquiries

List your organization's inquiries.

Listing inquiries

require 'http'
response = HTTP.
  headers('Authorization': "Bearer #{api_key}").
  get('https://withpersona.com/api/v1/inquiries?page[after]=inq_4eC39HqLyjWDarjtT1zdp7dc&page[size]=100')
inquiry = JSON.parse(response.body)
import requests
response = requests.get(
  'https://withpersona.com/api/v1/inquiries?page[after]=inq_4eC39HqLyjWDarjtT1zdp7dc&page[size]=100',
  headers: { 'Authorization': 'Bearer {}'.format(api_key) },
)
inquiry = response.json()
const request = require("request");
request(
  {
    json: true,
    url: `https://withpersona.com/api/v1/inquiries?page[after]=inq_4eC39HqLyjWDarjtT1zdp7dc&page[size]=100`,
    headers: { Authorization: `Bearer ${apiKey}` }
  },
  (err, res, body) => {
    inquiry = res;
  }
);

Example response

{
  "data": [
    {
      "type": "inquiry",
      "id": "inq_RQ4jHMYx8wHMCSjQ2M43jf4r",
      "attributes": {
        "status": "completed",
        "reference-id": null,
        "created-at": "2019-01-18T18:37:23.000Z",
        "completed-at": "2019-01-18T18:38:43.000Z",
        "expired-at": null
      },
      "relationships": {
        "template": {
          "data": {
            "type": "template",
            "id": "tmpl_WMmFNiixxX1doLfj5KDi6dQq"
          }
        },
        "verifications": {
          "data": [
            {
              "type": "verification/phone-number",
              "id": "ver_xw93bRD8EjLp8uqsSA2PoEoC"
            },
            {
              "type": "verification/basic",
              "id": "ver_uRmPFecaKgzjmsWGzAVEhrPY"
            }
          ]
        },
        "reports": {
          "data": [
            {
              "type": "report/profile",
              "id": "rep_i2a3jQQlMSLp38XOnweLQm3z"
            },
            {
              "type": "report/watchlist",
              "id": "rep_Iun32mW34j2lknEFUnqiupNW"
            }
          ]
        }
      }
    }
    // ...
  ]
  "links": {
    "next": "https://withpersona.com/api/v1/inquiries?page[before]=inq_RQ4jHMYx8wHMCSjQ2M43jf4r&page[size]=100",
    "prev": "https://withpersona.com/api/v1/inquiries?page[after]=inq_4eC39HqLyjWDarjtT1zdp7dc&page[size]=100",
  }
}

An inquiry represents one user’s journey through the Persona flow. The index route lists all inquiries in sorted order, with the most recent inquiries appearing first. Note that this endpoint aggregates inquiries across all templates.

filter[reference-id]
optional
Only return inquiries for the given reference ID.
page[after]
optional
A cursor for use in pagination. `page[after]` is the inquiry ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, ending with `inq_bar`, your subsequent call can include `page[after]=inq_bar` in order to fetch the next page of the list.
page[before]
optional
A cursor for use in pagination. `page[before]` is the inquiry ID that defines your place in the list. For instance, if you make a list request and receive 100 objects, starting with `inq_bar`, your subsequent call can include `page[before]=inq_bar` in order to fetch the previous page of the list.
page[size]
optional
A limit on the number of inquiries returned. Limit can range between 1 and 100 (default is 10).

Inquiries are created when the user begins to verify their identity. Check for the following statuses determine whether the user has finished the flow.

pending When the server has finished all processing, the report will become ready.
completed When a user has completed the required verifications, their inquiry becomes completed.
expired If a user does not finish the flow, their inquiry expires after 24 hours.

GET /api/v1/inquiries/<inquiry-id>

Get details of a specific inquiry.

Getting an inquiry

require 'http'
response = HTTP.
  headers('Authorization': "Bearer #{Rails.credentials.persona.fetch(:api_key)}").
  get("https://withpersona.com/api/v1/inquiries/#{params.fetch(:'inquiry-id')}")
inquiry = JSON.parse(response.body)
import requests
response = requests.get(
  'https://withpersona.com/api/v1/inquiries/{}'
    .format(request.GET.get('inquiry-id')),
  headers: { 'Authorization': 'Bearer {}'.format(api_key) },
)
inquiry = response.json()
const request = require("request");
request(
  {
    json: true,
    url: `https://withpersona.com/api/v1/inquiries/${req.query["inquiry-id"]}`,
    headers: { Authorization: `Bearer ${apiKey}` }
  },
  (err, res, body) => {
    inquiry = res;
  }
);

Example response

{
  "data": {
    "type": "inquiry",
    "id": "inq_RQ4jHMYx8wHMCSjQ2M43jf4r",
    "attributes": {
      "status": "completed",
      "reference-id": null,
      "created-at": "2019-01-18T18:37:23.000Z",
      "completed-at": "2019-01-18T18:38:43.000Z",
      "expired-at": null
    },
    "relationships": {
      "template": {
        "data": {
          "type": "template",
          "id": "tmpl_WMmFNiixxX1doLfj5KDi6dQq"
        }
      },
      "verifications": {
        "data": [
          {
            "type": "verification/phone-number",
            "id": "ver_xw93bRD8EjLp8uqsSA2PoEoC"
          },
          {
            "type": "verification/basic",
            "id": "ver_uRmPFecaKgzjmsWGzAVEhrPY"
          }
        ]
      },
      "reports": {
        "data": [
          {
            "type": "report/profile",
            "id": "rep_i2a3jQQlMSLp38XOnweLQm3z"
          },
          {
            "type": "report/watchlist",
            "id": "rep_Iun32mW34j2lknEFUnqiupNW"
          }
        ]
      }
    }
  }
}

To retrieve a single inquiry, use the inquiry-id passed to you from the Persona flow. In the embedded flow, the inquiry-id is the first parameter of the onStart callback. In the hosted flow, the inquiry-id is a query parameter in the onSuccess callback.

POST /api/v1/inquiries/<inquiry-id>/resume

Resume an inquiry. Returns a session token.

Resuming an inquiry

require 'http'
response = HTTP.
  headers('Authorization': "Bearer #{Rails.credentials.persona.fetch(:api_key)}").
  post("https://withpersona.com/api/v1/inquiries/#{params.fetch(:'inquiry-id')}/resume")
inquiry = JSON.parse(response.body)
import requests
response = requests.post(
  'https://withpersona.com/api/v1/inquiries/{}/resume'
    .format(request.GET.get('inquiry-id')),
  headers: { 'Authorization': 'Bearer {}'.format(api_key) },
)
inquiry = response.json()
const request = require("request");
request.post(
  {
    json: true,
    url: `https://withpersona.com/api/v1/inquiries/${
      req.query["inquiry-id"]
    }/resume`,
    headers: { Authorization: `Bearer ${apiKey}` }
  },
  (err, res, body) => {
    inquiry = res;
  }
);

Example response

{
  "data": {
    "type": "inquiry",
    "id": "inq_RQ4jHMYx8wHMCSjQ2M43jf4r",
    "attributes": {
      "status": "pending",
      "reference-id": null,
      "created-at": "2019-01-18T18:37:23.000Z",
      "completed-at": null,
      "expired-at": null
    },
    "relationships": {
      "template": {
        "data": {
          "type": "template",
          "id": "tmpl_WMmFNiixxX1doLfj5KDi6dQq"
        }
      },
      "verifications": {
        "data": [
          {
            "type": "verification/phone-number",
            "id": "ver_xw93bRD8EjLp8uqsSA2PoEoC"
          },
          {
            "type": "verification/basic",
            "id": "ver_uRmPFecaKgzjmsWGzAVEhrPY"
          }
        ]
      },
      "reports": {
        "data": []
      }
    }
  },
  "meta": {
    "session-token": "..."
  }
}

To resume an existing inquiry in any flow, use the appropriate parameter to specify the inquiry-id. If the inquiry is in a pending state, you must also specify a session token generated from this endpoint.

DELETE /api/v1/inquiries/<inquiry-id>

Delete an inquiry.

Deleting an inquiry

require 'http'
response = HTTP.
  headers('Authorization': "Bearer #{Rails.credentials.persona.fetch(:api_key)}").
  delete("https://withpersona.com/api/v1/inquiries/#{params.fetch(:'inquiry-id')}")
inquiry = JSON.parse(response.body)
import requests
response = requests.delete(
  'https://withpersona.com/api/v1/inquiries/{}'
    .format(request.GET.get('inquiry-id')),
  headers: { 'Authorization': 'Bearer {}'.format(api_key) },
)
inquiry = response.json()
const request = require("request");
request.delete(
  {
    json: true,
    url: `https://withpersona.com/api/v1/inquiries/${req.query["inquiry-id"]}`,
    headers: { Authorization: `Bearer ${apiKey}` }
  },
  (err, res, body) => {
    inquiry = res;
  }
);

Example response

{
  "data": {
    "type": "inquiry",
    "id": "inq_RQ4jHMYx8wHMCSjQ2M43jf4r",
    "attributes": {
      "status": "completed",
      "reference-id": null,
      "created-at": "2019-01-18T18:37:23.000Z",
      "completed-at": "2019-01-18T18:38:43.000Z",
      "expired-at": null
    },
    "relationships": {
      "template": {
        "data": {
          "type": "template",
          "id": "tmpl_WMmFNiixxX1doLfj5KDi6dQq"
        }
      },
      "verifications": {
        "data": [
          {
            "type": "verification/phone-number",
            "id": "ver_xw93bRD8EjLp8uqsSA2PoEoC"
          },
          {
            "type": "verification/basic",
            "id": "ver_uRmPFecaKgzjmsWGzAVEhrPY"
          }
        ]
      },
      "reports": {
        "data": [
          {
            "type": "report/profile",
            "id": "rep_i2a3jQQlMSLp38XOnweLQm3z"
          },
          {
            "type": "report/watchlist",
            "id": "rep_Iun32mW34j2lknEFUnqiupNW"
          }
        ]
      }
    }
  }
}

Permanently deletes an inquiry and all associated verifications. It cannot be undone. This endpoint can be used to comply with privacy regulations such as GDPR / CCPA or to enforce data privacy.

Reports Resource

GET /api/v1/reports/<report-id>

Getting a report

Get a report.

require 'http'
response = HTTP.
  headers('Authorization': "Bearer #{Rails.credentials.persona.fetch(:api_key)}").
  get("https://withpersona.com/api/v1/reports/#{params.fetch(:'report-id')}")
report = JSON.parse(response.body)
import requests
response = requests.get(
  'https://withpersona.com/api/v1/reports/{}'
    .format(request.GET.get('report-id')),
  headers: { 'Authorization': 'Bearer {}'.format(api_key) },
)
report = response.json()
const request = require("request");
request(
  {
    json: true,
    url: `https://withpersona.com/api/v1/reports/${req.query["report-id"]}`,
    headers: { Authorization: `Bearer ${apiKey}` }
  },
  (err, res, body) => {
    report = res;
  }
);

Profile report response

{
  "data": {
    "type": "report/profile",
    "id": "rep_i2a3jQQlMSLp38XOnweLQm3z",
    "attributes": {
      "status": "pending",
      "created-at": "2019-02-13T20:45:19.000Z",
      "country-code": "US",

      ...
    }
  }
}

Watchlist report response

{
  "data": {
    "type": "report/watchlist",
    "id": "rep_Iun32mW34j2lknEFUnqiupNW",
    "attributes": {
      "status": "ready",
      "created-at": "2019-02-10T08:15:26.000Z",
      "country-code": "US",
      "searched_lists": [...],

      ...
    }
  }
}

A report represents additional information pulled on the verified user. An inquiry can contain many reports. The attributes available for any given report depends on its type. Each inquiry’s relationships field lists the ids of all associated reports.

Reports are created and processed asynchronously when an inquiry completes. Check for the following statuses to determine whether a report is ready.

pending While the server processes the report, the status will be pending.
ready When the server has finished processing the report, the status will be ready.

Verifications Resource

GET /api/v1/verifications/<verification-id>

Get details into a verification.

Getting a verification

require 'http'
response = HTTP.
  headers('Authorization': "Bearer #{Rails.credentials.persona.fetch(:api_key)}").
  get("https://withpersona.com/api/v1/verifications/#{params.fetch(:'verification-id')}")
verification = JSON.parse(response.body)
import requests
response = requests.get(
  'https://withpersona.com/api/v1/verifications/{}'
    .format(request.GET.get('verification-id')),
  headers: { 'Authorization': 'Bearer {}'.format(api_key) },
)
verification = response.json()
const request = require("request");
request(
  {
    json: true,
    url: `https://withpersona.com/api/v1/verifications/${
      req.query["verification-id"]
    }`,
    headers: { Authorization: `Bearer ${apiKey}` }
  },
  (err, res, body) => {
    verification = res;
  }
);

Example phone number verification response

{
  "data": {
    "type": "verification/phone-number",
    "id": "ver_uY5ZGNUFvdJ1TU7KsVRS1RrD",
    "attributes": {
      "status": "passed",
      "created-at": "2019-02-13T20:45:19.000Z",
      "country-code": "US",

      ...
    }
  }
}

Example driver license verification response

{
  "data": {
    "type": "verification/driver-license",
    "id": "ver_Fi4XSWdgbPXZHxRKYtfT3N3M",
    "attributes": {
      "status": "passed",
      "created-at": "2019-02-10T08:15:26.000Z",
      "country-code": "US",

      ...
    }
  }
}

A verification represents one of multiple checks done by a user. An inquiry contains one or more verifications. The attributes available for any given verification depends on its type. Each inquiry’s relationships field lists the ids of all associated verifications. To authenticate when fetching photo URLs, pass the same Authorization header.

Verifications change statuses as the user progresses through the flow. Check for the following statuses to monitor progress and find completed results.

initiated While a user has is still completing the verification, the status will be initiated.
submitted When a user has submitted their information, their verification becomes completed. During this status, the server is verifying their information.
passed Once the server has verified the user’s information, the verification passes.
failed If the server fails to verify the user’s information, the verification fails.