Yardstik API (1.0)

Download OpenAPI specification:Download

Introduction

Yardstik is a modern platform offering a mix of screening, fraud, and compliance products designed to help you vet candidates ASAP. Our REST API utilizes predictable, resource-oriented URLs which are authenticated via a single API key and leverage JSON in all responses.

For more about Yardstik, visit yardstik.com. If you're ready to start your integration, reach out to support@yardstik.com to speak with our Enterprise Solutions team.

Get Your Accounts

Our Enterprise Solutions team will work with you to create your accounts (one on our staging environment and one on production). For platforms, we'll also guide you through your subaccount setup. In order to credential your accounts, we'll need a few key pieces of information from you:

Legal Business Information
The full legal name of your business and EIN as they appear on your government registration documents

Account Owner Contact Info
The first/last name and email address for the person who'll be managing your Yardstik account

Permissible Purpose
As a consumer reporting agency in the United States, Yardstik is regulated by federal, state, and local levels of government, primarily by the Fair Credit Reporting Act (FCRA). When requesting a consumer report on an individual, the FCRA requires that we collect from you a "permissible purpose", which is just a fancy way of saying: you need to have a specific, legally-protected reason for ordering consumer reports.

Here are some examples

  • Employment: Includes the hiring, retention, reassignment, and/or promotion of employees (can apply to volunteers and interns as well)
  • Written Instructions of the Consumer: For contracting engagements or access to your platform

Desired Screenings
Based on the types of screenings you'd like to run on your candidates, our team will create the appropriate Packages for your account.

Once you're up and running, you'll have access to the Yardstik API as well as the admin features at app.yardstik.com where you can use developer tools, manage reports, view metrics, etc.

Get Your API Keys

Once your staging and production accounts have been created, you'll have access to the Developer Tools section in Yardstik. You'll need to create an API key in each of your accounts, which you'll use to authenticate your requests in each environment. Make sure to store your full API key somewhere secure (like a password manager). For security reasons, the only time you'll see the full key displayed in Yardstik is right after you create it.

Remember, anyone with access to your API keys can make API calls on your behalf. Therefore, we recommend these best practices to keep your API keys safe

  • Never embed API keys directly in code
  • Never store API keys in files inside your source code
  • Delete unused API keys
  • Periodically regenerate your API keys
  • Always encrypt your API keys
  • Restrict API key access to only those that need it
  • Never share your API keys, not even with us

If you believe your API key has been compromised, create a new API key and switch over to using that one, then delete your compromised API key.

Create API Keys

alt text

  • Name Your Key - Enter a descriptive name for your API key
  • Generate the Key - Click the "Create" button
  • Save Securely - When the API key appears in the popup modal:
    • Click the "Copy" button
    • Store the key in a password manager or secure location

alt text

IMPORTANT: This will be the only time you'll see the complete API key. Be sure to save it immediately.

Authenticate With Yardstik

Yardstik uses HTTP API key authentication. To authenticate, include your API key in the header of all your API requests using the format "Account {API_KEY}" (with the word "Account" preceding your API key).

Example Authenticated Request

curl -X GET https://api.yardstik.com/accounts \
    -H 'Authorization: Account [YOUR_API_KEY]' \
    -H 'Accept: application/json'

Example Authenticated Response

[
    {
        "id": "cc0df11d-a502-47ec-8aa1-ba0738030ca7",
        "account_name": "Global Dynamics",
        "first_name": "Nathan",
        "last_name": "Stark",
        "email_address": "nathan.stark@globaldynamics.com",
        "url": "https://www.globaldynamics.com",
        "tech_email_address": "it@globaldynamics.com",
        "support_email_address": "support@globaldynamics.com",
        "support_phone": "541-555-4376",
        "actions_email_address": "hr@globaldynamics.com",
        "compliance_email_address": "hr@globaldynamics.com",
        "logo": "https://vignette.wikia.nocookie.net/eureka/images/d/dd/Global_dynamics.jpg",
        ...
    },
    ...
]

If you're seeing a similar response, you're authenticated and can make requests to our API.

Order Your First Report

Reports are the main resource you'll care about. They contain the findings for each candidate, whether you're running a background check, identity verification, or a more specialized screening like Social Media. That being said, there are some other resources which need to be in place before you can order a report.

Candidates
First off, you'll need to create a candidate with whom you'll associate the report. This is the person whose information we'll use when we run the report.

Packages
Based on your business requirements, our team will choose the appropriate screenings and group them into packages for your account. Each time you order a report, you'll specify a package; this will determine the candidate information required for that report. For example, if you want to run a report with a package that includes a Motor Vehicle Record screening, we'll need the candidate's Driver's License Number and State.

Reports With Invitations
If you'd like Yardstik to invite your candidate to fill out their information in Yardstik's UI, create an invitation and we'll automatically create a corresponding report to go with it. Based on the package you specify, we'll generate a form where the candidate will fill out all the information required to run their report. Their invitation will include a link to their unique form.

Reports Without Invitations
If you don't need Yardstik to send an invitation because you've embedded Yardstik's UI and you'd like your candidate to fill out their information as part of your in-app workflow, you can skip the invitation call and create a report instead. Once the report is created, you can grab the apply url from the metadata in the response and serve that up in your embedded view.

Alternatively, if you'd like to provide all the candidate information yourself (rather than asking the candidate to fill it in), you can create a report with the parameter account_candidate_consented: true. In your API call, you'll need to send along all the information required to run the report, based on the package you specify. To order reports this way, you'll need this feature enabled for your account. Your organization will assume full legal responsibility for collecting the FCRA disclosure and consent agreements from each candidate.

Sample Redirect Flow
To create a candidate, collect the candidate's information and POST it to our /candidates route. We'll return a candidate ID. Using the candidate id along with your account id and one of your package ids, POST this data to the /reports endpoint to order a new report. This will return the apply url in the metadata, which you can use to direct the candidate to their unique intake form.

Once the candidate fills out the form and submits disclosures, the report will begin processing.

alt text

Using Webhooks

Listen for Webhook Events

Yardstik uses webhooks to publish events any time certain situations occur. You can subscribe to receive those events, allowing your application to react in real time.

To subscribe, use the Webhooks tool to register your URL to receive POST requests for your desired event type. Repeat for each event type you'd like to listen for.

Webhook event types Publishes an event each time this occurs in your account
candidate.created a candidate is successfully created
candidate.updated a candidate is updated, e.g. name has changed
candidate.deleted a candidate is deleted
candidate.consented a candidate submits signed legal docs, including consent to a background check
candidate.paid a candidate successfully completes payment (only applies in scenarios where the candidate is responsible to pay for their report)
transaction.created a payment transaction is created
transaction.updated a payment transaction is updated
transaction.deleted a payment transaction is deleted
invitation.created an invitation (and its corresponding report) is successfully created
invitation.updated an invitation to a report is updated, e.g. an expired invitation is refreshed
invitation.deleted an invitation to a report is deleted
report.created a report is successfully created
report.updated a report is updated, e.g. the report status has changed Note: this is likely the most important event type to listen for, as it sends lots of important updates about each report
report.completed a report reaches one of the "done" statuses Note: if you don't want to be notified of every report update, but you want to know when each report is completed, you can listen for this event type instead of report.updated
report.submitted a report is submitted (the candidate provides all required data)
report.deleted a report is deleted
monitor.created a monitor is created
monitor.updated a monitor is updated
monitor.deleted a monitor is deleted
monitor_record.created a monitor returns a new record about one of your candidates
monitor_record.updated a monitor record is updated
monitor_record.deleted a record a monitor returned about one of your candidates is deleted
account.updated your account is updated
sub_account.active a subaccount is activated by the Yardstik team

Here's some more info about the Webhooks resource

IP Addresses

The full list of IP addresses that webhook notifications may come from is:

3.134.163.29
3.23.8.14
52.15.242.117
3.143.82.60
18.119.6.96
18.117.198.82

Webhook Signing

Using Webhook Signing, you can ensure that requests sent to your application are from Yardstik and have not been tampered with. You should use the webhook signature to avoid processing requests that are not from Yardstik and to protect your application from bad actors.

Enabling Webhook Signing

To enable webhook signatures, create an API key named WEBHOOK_SIGNATURE (see: Get Your API Keys). Make sure you copy the API key to a safe location before closing the modal, as it will not be visible again later. Yardstik will automatically use your WEBHOOK_SIGNATURE API key to sign the webhook payload using an SHA-256 HMAC hex digest. For platform accounts and any subaccounts, each one will each need their own WEBHOOK_SIGNATURE API key in order to receive the signature.

You'll use this API Key to generate your own HMAC hash against the payload you recieved to verify that

  1. The secret API key you generated was used to sign the request, proving it came from Yardstik
  2. The message payload has not been modified, protecting against man-in-the-middle attacks

How to validate webhooks

The HMAC hash value that Yardstik uses to sign your webhook will be included in the headers of the request as: x-yardstik-webhook-signature

You'll use theWEBHOOK_SIGNATURE API key to generate an HMAC hash of the body of the request and validate the authenticity of that webhook.

If the value of x-yardstik-webhook-signature is: a1342d29ce3b28c4416482f64a3e8b9f452a22755646d7ca9a553e654676b693
Then your app should also calculate the value as: a1342d29ce3b28c4416482f64a3e8b9f452a22755646d7ca9a553e654676b693

As long as those two values are equal, you should accept the request. If they differ, your application should reject the webhook and stop processing.

A verification function can be used to validate the HMAC-SHA256 value using a crypto library of your choice, such as the Node.js crypto module. The function should calculate a hash using your WEBHOOK_SIGNATURE API key, and ensure that the result matches the hash sent by Yardstik.

Putting it all together

Example Code

This is a simple Node.js example showcasing how to validate webhook signatures using Crypto libraries behind an Express server

const crypto = require('crypto');
const express = require('express');
const app = express();
const port = 3000;

app.use(express.json());

app.post('/', (req, res) => {
    const SIGNATURE = req.get('x-yardstik-webhook-signature');
    const BODY = req.body;
    console.log(BODY);

    const HASH = crypto.createHmac('sha256', process.env.WEBHOOK_SIGNATURE).update(JSON.stringify(BODY)).digest('hex');

    console.log(`This is the x-yardstik-webhook-signature: ` + SIGNATURE);
    console.log(`This is your application's calculated hash: ` + HASH);

    // Respond with a 200, if you do not respond with a success code Yardstik will retry the request.
    res.sendStatus(200);
})

app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})

Example Output

Example app listening on port 3000
{
  id: '42eff0e4-9bca-45da-a143-2d4e1e271d88',
  event: 'updated',
  resource_id: 'efa32155-f8cb-4331-954c-11db22ae773d',
  account_id: '16fab437-3ab5-4ee1-8a47-16ab41410a5e',
  account_name: 'Example Account',
  resource_type: 'report',
  resource_url: 'https://api.yardstik.com/reports/efa32155-f8cb-4331-954c-11db22ae773d',
  status: 'processing',
  timestamp: '2024-07-10T18:56:44Z'
}
This is the x-yardstik-webhook-signature: a1342d29ce3b28c4416482f64a3e8b9f452a22755646d7ca9a553e654676b693
This is your application's calculated hash: a1342d29ce3b28c4416482f64a3e8b9f452a22755646d7ca9a553e654676b693

The function above uses an environment variable name WEBHOOK_SIGNATURE that contains the API key you created. The app will extract the body from the request sent to the / endpoint and use the Node.js crypto library and your API key to create its own hex digest that should match the header included in the request.

Using a tool like ngrok, you can start this locally and receive calls from our API to your dev workstation to verify that webhook signing is working. Note: Ask your security team before running port forwarding applications that expose your app to the internet!

Additional sample webhook responses, POST bodies, and more are available in the Webhooks resource section.

Support

If your webhook signature has been lost or compromised, you can decommission it in Yardstik and create a new one. Here are some best practices for keeping your keys safe. If your webhook signature isn't working, don't hesitate to reach out to support@yardstik.com.

Webhook Examples

Listening for candidate.created Webhook

Register a webhook for the candidate.created event type using the Create Webhook Route. This webhook will be triggered when a new candidate is created.

Example Create candidate.created Webhook Response

{
    "id": "a87b1e1a-e9aa-4929-9f30-ad1f9b481a8c",
    "url": "https://www.yourcompany.com/webhooks/candidate_created",
    "webhook_type": "candidate.created"
}

Example candidate.created Webhook POST body

{
    "id": "dcd9a3db-d885-4695-8a92-5556126000c5",
    "event": "created",
    "resource_id": "67cae505-0bac-4eaf-a255-95421d04e303",
    "account_id": "e829bc40-89eb-4054-94e3-7e826373f8ec",
    "account_name": "Venture Industries",
    "resource_type": "candidate",
    "resource_url": "https://api.yardstik.com/candidates/67cae505-0bac-4eaf-a255-95421d04e303",
    "status": "",
    "timestamp": "2025-04-18T15:30:46.641Z"
}

Now that the webhook was triggered, you should receive the response body presented above. You can use this to get the candidate ID from the resource_url key.

Listening for candidate.updated Webhook

To receive automatic notifications whenever a candidate's information changes, register for the candidate.updated webhook. This service will promptly alert you when any public field is modified, delivering the updates in array format under the fields_changed key.

{
    "id": "9ab907a8-9ab0-4fc6-b461-e4d1c889ad7c",
    "event": "updated",
    "resource_id": "58267943-33d3-472d-90e8-2d9f962dfd1e",
    "account_id": "e829bc40-89eb-4054-94e3-7e826373f8ec",
    "account_name": "Venture Industries",
    "resource_type": "candidate",
    "resource_url": "https://api.yardstik.com/candidates/58267943-33d3-472d-90e8-2d9f962dfd1e",
    "status": "",
    "timestamp": "2025-04-11T20:28:31.878Z",
    "fields_changed": [
      "updated_at",
      "no_middle_name",
      "ssn",
      "phone",
      "date_of_birth"
    ]
  }

Fields That Trigger the candidate.update Webhook

The candidate.updated webhook will be triggered when any of the following candidate fields are modified:

  • first_name
  • middle_name
  • no_middle_name
  • last_name
  • email
  • phone
  • ssn
  • date_of_birth
  • gender
  • driver_license_number
  • driver_license_state
  • previous_driver_license_number
  • previous_driver_license_state
  • external_id

Listening for report.created Webhook

Let's say you also want to be notified each time a report is created. To do so, you can register for the report.created event type.

Example Create report.created Webhook Request

curl -X POST https://api.yardstik.com/accounts/[YOUR_ACCOUNT_ID]/webhooks \
    -H 'Authorization: Account [YOUR_API_KEY]' \
    -H 'Accept: application/json'
    -H 'Content-Type: application/json' \
    --data-raw '{
        "webhook_type_id": "48a9b600-e7ff-4039-bcb2-04cddab97ba8",
        "url": "https://www.yourcompany.com/webhooks/report_created"
    }'

Again, you would create a webhook for report.created.

Example Create report.created Webhook Response

{
    "id": "89ade71a-e9aa-4929-9f30-ad1f93245abf",
    "url": "https://www.yourcompany.com/webhooks/report_created",
    "enabled": true,
    "webhook_type": "report.created",
    "webhook_description": "A report has been created."
}

And once a report is created, you will be sent this

Example report.created Webhook POST body

{
    "id": "e7331072-7865-4dca-9cd2-1643656a84c7",
    "event": "created",
    "resource_id": "e7e0b119-7714-44a2-900e-9b98a4e5b1c6",
    "account_id": "e829bc40-89eb-4054-94e3-7e826373f8ec",
    "account_name": "Venture Industries",
    "resource_type": "report",
    "resource_url": "https://api.yardstik.com/reports/e7e0b119-7714-44a2-900e-9b98a4e5b1c6",
    "status": "created",
    "timestamp": "2025-04-11T20:26:58.248Z"
}

Listening for report.updated Webhook

To receive automatic notifications whenever a report has been updated, register for the report.updated webhook.

Example report.updated Webhook POST body

{
    "id": "86bafd62-751a-4465-8b9b-811b08cfbf26",
    "event": "updated",
    "resource_id": "48f51248-fa8c-4f92-bdd6-ea2f5d6e1bd0",
    "account_id": "e829bc40-89eb-4054-94e3-7e826373f8ec",
    "account_name": "Venture Industries",
    "resource_type": "report",
    "resource_url": "https://api.yardstik.com/reports/48f51248-fa8c-4f92-bdd6-ea2f5d6e1bd0",
    "status": "queued",
    "timestamp": "2025-04-11T20:28:53.249Z"
}

Report Lifecycle

From creation to completion, each report has the potential to go down a few different paths. The best way to know the current state of any given report is by checking its status. If you've opted to use an invitation with your report, the invitation also has a status. The happiest path is when no records are found on the candidate and the report sails through to a clear status. But since that's not always the case, we want to help you understand what else can happen.


Invitation Statuses
Report Statuses
Downloading Report PDFs
Disputes
Adverse Action

Invitation Statuses

Status Description
invite_sent The invitation sent successfully, but the candidate hasn't taken action yet
viewed The candidate viewed the invitation, but hasn't clicked through to the form yet
clicked The candidate clicked the invitation link, but hasn't completed the intake form
completed The candidate submitted the intake form and the report will begin processing
canceled The invitation (and its corresponding report) was canceled, preventing the candidate from accessing the form
expired The invitation expired, preventing the candidate from accessing the form

Here's some more info about the Invitations resource

Report Statuses

There are many different statuses a candidate's report can be in. Here, we've grouped the statuses to illustrate what they mean in relation to the report's progress. The detailed descriptions of each status are below.

Not Started Moving Along Paused Action Needed Done
Created
Expired
Queued
Processing
Pending
Pre-Adverse
Approved To Process
Info Requested
Dispute
Pending Approval
YS QA Review
Exception
On Hold
Consider Clear
Proceed
Final Adverse
Canceled
Pass
Fail
Status Description
created The report was successfully created and is ready to receive candidate information/consent
expired The report was created, but didn't receive candidate information/consent before its invitation's expiration date. An expired report can be reopened by refreshing its invitation. For more about expiration and how to customize, see Invitations.
queued The report is waiting for the next screening to start processing
processing Our verifications team is processing the report
pending We're waiting on a response from a third party (e.g. a court runner is retrieving records for a County Criminal check)
pre_adverse In response to the report being in consider status, one of your admins decided to kick off the Adverse Action process
approved_to_process On reports where the candidate is a minor, this indicates that their guardian provided consent for us to process their report
info_requested The candidate information we received wasn't sufficient to run the report. We've asked the candidate to provide the information we need and are waiting for their response.
dispute The candidate has filed a dispute regarding the accuracy or completeness of the results in their report. Our verifications team is investigating.
pending_approval On reports where the candidate is a minor, this indicates that we're waiting for their guardian to provide consent for us to process the report
ys_qa_review If you've opted for us to manage the Adverse Action process for you, this indicates that results have been added to the report which may disqualify the candidate. Our verifications team is reviewing and will decide next steps based on your adjudication matrix.
exception There is an issue with the report data (e.g. SSN is incorrect) that is preventing the report from processing. Our verifications team will contact the candidate for the correct information.
on_hold The report has been placed on hold. Processing is paused until the hold is removed. A hold can be placed during report creation by setting on_hold to true. To remove the hold and resume processing, use the Remove hold from report endpoint.
consider Results have been added to the report which may disqualify the candidate. One of your admins needs to review and decide whether to set the report to proceed or kick off the Adverse Action process.
clear The report has finished processing and no disqualifying results were found
proceed In response to the report being in consider status, one of your admins decided to set the report to proceed, indicating that the results found do not disqualify the candidate
final_adverse The Adverse Action process is complete and it has been determined that the results found disqualify the candidate
canceled The report was canceled and no decision has been made about the candidate. No additional activity will occur on this report.
pass On reports where there are exclusively pass/fail outcomes (e.g. reports with Identity Verification and no additional screenings), pass indicates a positive "done" state
fail On reports where there are exclusively pass/fail outcomes (e.g. reports with Identity Verification and no additional screenings), fail indicates a negative "done" state

Here's some more info about the Reports resource

Downloading Report PDFs

Yardstik provides access to report PDFs via the report_url field in our reports endpoint. This URL appears in two possible locations within the response metadata, depending on the report’s structure. We recommend you check the first location, then if needed, check the second location.

  1. Top-level: If the report has a single PDF for all of its screenings, the top-level report_url will contain the download link
  2. Nested: If the report has a separate PDF for each of its screenings, the top-level report_url will be null, and you’ll need to access the individual report_url within each report_screening to retrieve the download link(s)

Disputes

Consolidating data from several sources into a report is a complex process that can sometimes lead to unintended discrepancies between the collected information and what the consumer (i.e. candidate) knows or believes to be true. This is why the FCRA provides consumers a mechanism to dispute inaccuracies. As a consumer reporting agency, Yardsik has an obligation to provide accurate reports. When a candidate logs a dispute, we take action right away to determine whether the dispute is frivolous or if a correction to the report is necessary. We take this obligation seriously and have established internal processes to appropriately investigate, respond to, and resolve disputes in a timely manner. No action from you is needed during the dispute process.

Adverse Action

When our findings include results which may disqualify the candidate, we set the report status to consider. This gives your organization the opportunity to review the report and decide whether the candidate will proceed in your process or not. If you determine that they've been disqualified, create an adverse action (via the Yardstik UI or API) and specify which violations factored into your decision. This will set the report status to pre-adverse and kick off the FCRA-required legal process called "Adverse Action".

Adverse Action offers the candidate the opportunity to understand exactly which results disqualified them. When the adverse action is created, Yardstik will send the candidate an email containing all the FCRA-required information, including the violations you specified. The candidate will then have a set number of days (based on your account notifications setting) during which they may respond to you and provide additional information about themselves for you to consider. If you have your own process for completing an "individualized assessment", you'll want to complete that within this timeframe. The minimum number of days prescribed by the FCRA is 7, but you can set it higher.

If you change your mind during this time, you can cancel the adverse action (via the Yardstik UI or API), which will automatically move the report to the next stage. If you take no action, when the timeframe elapses the report will automatically be completed with a final adverse status and Yardstik will notify the candidate as such.

Here's some more info about the Adverse Actions resource

How do I know if the violations parameter is required?

In the response of the Get a Report Route, you'll have the adverse_action_settings key. Within is the mandatory_selection_of_violations key, which tells you whether or not you need to select violations when creating a new adverse action (most often, you do).

  adverse_action_settings: {
    "mandatory_selection_of_violations": true
  }

Which violations can I send?

In the response of the Get a Report Route, you'll have the report_screenings key, which is an array. For each report screening, you'll have an array of records, and inside each record you'll have an array of violations. (A record may not have violations. For example, a clear report probably won't have any violations.)

For each violation, you'll have a description and possibly a disposition and disposition_date.

You can map all the violations and select the ones you want to use for the adverse action.

Example of a full report response

{
    "id": "2ee83662-ea17-4233-95b9-288ee3fcb9ff",
    "reference_id": null,
    "candidate_id": "66bdcd96-adf8-4b8a-b599-ea0040a11b79",
    "status": "consider",
    "response_status": null,
    "submitted_at": "2021-04-26T22:33:18.269Z",
    "completed_at": "2021-04-30T15:44:24.333Z",
    "created_at": "2021-04-26T21:51:06.880Z",
    "decision": "",
    "usage_amount": null,
    "process_sequentially": true,
    "created_by": "cool_email@yardstik.com",
    "permissible_purpose": {
        "id": "a2c00755-b57e-40e1-9876-2c5640ce53be",
        "name": "Employment",
        "configurations": { "initial_status": "pre_adverse" }
      }
    },
    "candidate": {
        "id": "66bdcd96-adf8-4b8a-b599-ea0040a11b79",
        "full_name": "John Clean",
        "email": "cool_email@yardstik.com",
        "phone_masked": null,
        "date_of_birth_masked": "#######-12",
        "ssn_masked": null,
        "driver_license_number_masked": null,
        "full_address": {
            "address": "Mineapolis",
            "zip_code": "12345",
            "city": "Mineapolis",
            "state": "MN"
        },
    },
    "account_id": "d210dcdd-c9d4-4ced-a594-8402cb6a2ab4",
    "account_name": "Account Name",
    "report_url": null,
    "adverse_action_id": null,
    "report_screenings": [
        {
            "id": "e11b7d00-9b55-4879-8f6e-c3859f16347d",
            "type": "Criminal",
            "label": "CountyCriminal",
            "name": "CountyCriminal",
            "status": "consider",
            "order": 0,
            "decision": "Consider",
            "response_status": "ready",
            "created_at": "2021-04-26T22:33:18.218Z",
            "report_url": "",
            "elapsed_timing": "1 week",
            "records": [
                {
                    "category": {
                        "key": "County",
                        "value": "NY-NYOCA"
                    },
                    "messages": [],
                    "violations": [],
                    "personal_data": {},
                    "other_information": {
                        "jurisdiction": "NY-NYOCA",
                        "pending_notes": null
                    }
                }
            ],
            "comments": []
        },
        {
            "id": "c6851635-9ce4-47ce-a246-531fbe36aac0",
            "type": "Criminal",
            "label": "StateCriminalCourt",
            "name": "StateCriminalCourt",
            "status": "consider",
            "order": 0,
            "decision": "Consider",
            "response_status": "ready",
            "created_at": "2021-04-26T22:33:18.228Z",
            "report_url": "",
            "elapsed_timing": "1 week",
            "records": [
                {
                    "category": {
                        "key": "State",
                        "value": "NEW YORK"
                    },
                    "messages": [],
                    "violations": [
                        {
                            "comments": null,
                            "sentence": "24 Months Or Pretrial Intervention Next Court Date 01/01/2021",
                            "file_date": "2021-03-20",
                            "case_number": "ABCDEFG",
                            "description": "Felony",
                            "disposition": "Court: Pre Trial Intervention",
                            "jurisdiction": "NEW YORK",
                            "offense_date": null,
                            "count_offense": "1. Possession Of Schedule 4 Substance",
                            "dob_on_record": "XXXX-04-01",
                            "ssn_on_record": null,
                            "name_on_record": "MESS, HANK",
                            "disposition_date": "2021-03-20"
                        },
                        {
                            "description": "This one Has Only the description",
                        }
                    ],
                    "personal_data": {},
                    "other_information": {
                        "jurisdiction": "NEW YORK",
                        "pending_notes": null
                    }
                },
                {
                    "category": {
                        "key": "State",
                        "value": "MINNESOTA"
                    },
                    "messages": [],
                    "violations": [
                        {
                            "comments": null,
                            "sentence": "24 Months Or Pretrial Intervention Next Court Date 10/08/2015",
                            "file_date": "2014-08-20",
                            "case_number": "POIUYTREWQ",
                            "description": "Felony",
                            "disposition": "Court: Pre Trial Intervention",
                            "jurisdiction": "MINNESOTA",
                            "offense_date": null,
                            "count_offense": "1. Possession Of Schedule 4 Substance",
                            "dob_on_record": "XXXX-04-01",
                            "ssn_on_record": null,
                            "name_on_record": "MESS, HANK",
                            "disposition_date": "2015-09-18"
                        },
                        {
                            "comments": null,
                            "sentence": "24 Months Or Pretrial Intervention Next Court Date 10/08/2015",
                            "file_date": "2014-08-20",
                            "case_number": "502008CF012155YXYXMB",
                            "description": "Felony",
                            "disposition": "Court: Pre Trial Intervention",
                            "jurisdiction": "MINNESOTA",
                            "offense_date": null,
                            "count_offense": "2. Possession Of Schedule 2 Substance",
                            "dob_on_record": "XXXX-04-01",
                            "ssn_on_record": null,
                            "name_on_record": "MESS, HANK",
                            "disposition_date": "2014-09-18"
                        },
                        ...
                    ],
                    "personal_data": {},
                    "other_information": {
                        "jurisdiction": "MINNESOTA",
                        "pending_notes": null
                    }
                }
            ],
            "comments": []
        }
    ],
    "report_screenings_total": 2,
    "report_screenings_completed": 2,
    "elapsed_timing": "4 days",
    "package_name": "State Criminal Court",
    "comments": [],
    "course": {
        "has_courses": false,
        "courses_completed": false
    },
    "meta": {
        "entity": "https://app.yardstik.com/reports/1234....",
        "apply": "https://profile.yardstik.com/candidates/1234
    },
    "adverse_action_settings": {
        "mandatory_selection_of_violations": false
    }
}

Platforms and Subaccounts

If you're going to be operating as a platform account in Yardstik (where your customers will have their own Yardstik subaccounts "underneath" yours and they'll be the ones ordering reports), this section is for you. To manage subaccounts, you'll need this feature enabled for your account.

As a platform account, in your Yardstik account settings, you can add your logo and brand colors, which will alter the look of Yardstik for your subaccount admins.

Creating Subaccounts

Just like your own account, when you create a subaccount to represent one of your customers, you'll need to specify a permissible purpose and at least one package. Your subaccounts must use a subset of the permissible purposes and packages associated with your platform account. You'll also need to provide the credentialing information highlighted in the Get Your Accounts section.

Permissible Purposes
To see a list of your platform account's permissible purposes, check the Get Account Route. The response body will have an array of permissible_purposes with an id for each. When you create a subaccount, you'll provide the permissible_purpose_id for each permissible purpose you want the subaccount to be able to use.

Packages
Similarly, to see a list of your platform account's packages, check the Get Packages Route. The response body will have an array of account_packages with an id for each. When you create a subaccount, you'll provide an account_packages array with the id for each package you want the subaccount to be able to use. This will automatically create those packages within the subaccount, using the same configurations as the platform account.

Inside the account_packages array, your objects must follow this format:

  • id: the platform account package id
  • name: the name of the subaccount package (if not provided, this will inherit the name of the platform account package)
  • paid_by: defines who will pay for the report, with the possible options of account or candidate (if not provided, this will inherit the platform account package's setting) Note: if set to candidate, the candidate will be asked to provide their credit card information in a payment form. Their report won't start processing until we've successfully received their payment.

How to provide the account_packages parameter

  "account_packages": [
      {
          "id": "the UUID of one of the platform account packages",
          "name": "the name of the subaccount package",
          "paid_by": "candidate"
      },
      {
          "id": "the UUID of one of the platform account packages",
          "name": "if not provided, will inherit the platform account package's name",
          "paid_by": "account"
      }
  ]

Subaccount Owner
The subaccount owner will be set based on the email_address you provide when you create the subaccount. The owner will be responsible for signing legal documents and completing their subaccount setup in Yardstik.

Here's some more info about the Subaccounts Resource

Credentialing Subaccounts

Once the subaccount is created, Yardstik will send an email to the subaccount owner with a link to sign into Yardstik, where they'll be prompted to create their user account and sign applicable legal documents. Our onboarding team will review everything, and once it all looks good, they'll mark the subaccount credentialed. We'll notify the subaccount owner that they now have full access to Yardstik and can complete any additional setup, e.g. adding their brand settings, customizing their notifications, and inviting additional admin users.

Note: The API key generated when the subaccount is created won't be active until the subaccount is credentialed. Once the API key is active, you can use it to manage Yardstik resources via the API. To get notified when your subaccounts become credentialed, subscribe to the sub_account.active webhook event type.

Example Create sub_account.active Webhook Request

curl -X POST https://api.yardstik.com/accounts/[YOUR_ACCOUNT_ID]/webhooks \
    -H 'Authorization: Account [YOUR_API_KEY]' \
    -H 'Accept: application/json'
    -H 'Content-Type: application/json' \
    --data-raw '{
        "webhook_type_id": "48a9b600-e7ff-4039-bcb2-04cddab97ba8",
        "url": "https://www.yourcompany.com/webhooks/sub_account_active"
    }'

Example Create sub_account.active Webhook Response

{
    "id": "89ade71a-e9aa-4929-9f30-ad1f93245abf",
    "url": "https://www.yourcompany.com/webhooks/report_completed",
    "enabled": true,
    "webhook_type": "sub_account.active",
    "webhook_description": "A subaccount has been activated by Yardstik support."
}

Example sub_account.active Webhook POST body

{
  "id": "ca64ad1a-2018-41c4-b369-dd5c3c1581dc",
  "event": "active",
  "resource_id": "21e2830c-b886-4234-a31d-5fa316069b21",
  "account_id": "e829bc40-89eb-4054-943-7e826373f8ec",
  "account_name": "Venture Capital",
  "resource_type": "sub_account",
  "resource_url": "https://api.yardstik.com/accounts/21e2830c-b88e-42e-a5ed-5e316069b21"
}

Pagination

All top-level API resources have support for bulk fetches via list API methods. Yardstik uses page-based pagination via the page and per_page parameters. For example, you can list invitations, candidates, and reports.

Standard Request

Example Paginated Request

curl -X GET https://api.yardstik.com/invitations?page=2&per_page=25 \
    -H 'Authorization: Account [YOUR_API_KEY]' \
    -H 'Accept: application/json'

Paginated list API Request Parameters

Parameter Description
page The page number to retrieve.
type: integer
default: 1
per_page The number of records per page.
type: integer
default: 50
minimum: 1
maximum: 200

The page parameter determines which page of results will be returned in the response, and defaults to 1 if not set or set to an invalid type. If the value of page exceeds the total_number of pages available, the response will include an empty data array.

The per_page parameter determines the maximum number of results included in each response. The maximum value allowed is 200, with a default value of 50.

  • If there are fewer resources available than the per_page value, the response will include all available resources.
  • If you pass more than 200, the response will still include only 200.
  • If the per_page value is an invalid type, the default value of 50 is used.

Standard Response

Paginated list API Response Structure

Example Paginated Response

{
    "object": "list",
    "meta": {
        "page": 2,
        "per_page": 25,
        "total_count": 137,
        "total_pages": 6
    },
    "data": [
        {
            "object": "invitation",
            "id": "18e00b78-e620-4ecf-a01a-eb810f1b6751",
            ... additional response attributes based on resource schema
        },
        {
            "object": "invitation",
            "id": "d0102680-e2de-4af8-9034-f88ccb4ad267",
            ... additional response attributes based on resource schema
        },
        ... additional resources in the page
    ]
}

The standard response for all top-level list API requests will include at least these attributes

Attribute Description
object The type of object returned.
type: string
default: "list"
meta Metadata associated with the response.
Attributes:
Attribute Description
page The page number of the response.
type: integer
per_page The number of results per page.
type: integer
total_count The total number of resources available.
type: integer
total_pages The total number of pages available.
type: integer
data An array of the requested resources.
type: array
Notes: May be an empty array [] if no resources are available.

Filter and Sort

For all listing endpoints, we support a filtering query param called query that allows you to pass a field name and a matcher to use to filter the results down a specific set. We also support two query params called order_by and order that let you pass the field name you want to order by, and the direction you want the results ordered. When combined with Pagination, this allows you to build a custom UI for listing out resources.

Filtering

Parameter Description
query Field name to filter on.
type: string

Get only reports created before Jan 1 2021

curl -g -X GET \
  'https://api.yardstik.com/reports?query[created_at_lt]=2022-01-01' \
  -H 'Authorization: Account [YOUR_API_KEY]' \
  -H 'Accept: application/json'

Get only reports updated after Jan 1 2021

curl -g -X GET \
  'https://api.yardstik.com/reports?query[updated_at_gteq]=2022-01-01T00:00:00.000Z'
  -H 'Authorization: Account [YOUR_API_KEY]' \
  -H 'Accept: application/json'

Get reports created between March 23 2021 and April 1 2021

curl -g -X GET \
  'https://api.yardstik.com/reports?query[created_at_lt]=2021-04-01&query[created_at_gt]=2021-03-23' \
  -H 'Authorization: Account [YOUR_API_KEY]' \
  -H 'Accept: application/json'

Matchers

Predicates Description Notes
*_eq Equal query[status_eq]=created
Individual check, to do multiple see *_in
*_not_eq Not equal query[status_not_eq]=created
Individual check, to do multiple see *_not_in
*_lt Less than query[created_at_lt]=2022-01-01
*_lteq Less than or equal to query[created_at_lteq]=2022-01-01
*_gt Greater than query[created_at_gt]=2022-01-01
*_gteq Greater than or equal to query[created_at_gteq]=2022-01-01
*_null Is null query[completed_at_null]=true
*_not_null Is not null query[completed_at_not_null]=true
*_in Match any value in array query[status_in][]=clear&query[status_in][]=consider
*_not_in Match none of the values in array query[status_not_in][]=clear&query[status_not_in][]=consider
*_start Starts with query[status_start]=con
*_not_start Does not start with query[status_not_start]=con
*_end Ends with query[status_end]=sider
*_not_end Does not end with query[status_not_end]=sider
*_cont Contains query[status_cont]=con
*_not_cont Does not contain query[status_not_cont]=con
*_true Is true query[active_true]=true
*_false Is false query[active_false]=true

Sorting

Parameter Description
order_by Field name to sort on.
type: string
order Specifies the direction to sort.
Accepts asc and desc.
Defaults to asc when not passed
type: string

Order candidates by created_at in default ascending order (oldest first)

curl -g -X GET \
  'https://api.yardstik.com/reports?order_by=created_at
  -H 'Authorization: Account [YOUR_API_KEY]' \
  -H 'Accept: application/json'

Order candidates by updated_at in descending order (newest first)

curl -g -X GET \
  'https://api.yardstik.com/reports?order_by=updated_at&order=desc
  -H 'Authorization: Account [YOUR_API_KEY]' \
  -H 'Accept: application/json'

Combine it all together

You can use pagination, querying and ordering all on the same call to fully control the results. This will get all reports created between March 23, 2021 and April 1, 2021 and order them by the created_at field with newest first while limiting the results to 20 per page.

curl -g -X GET \
  'https://api.yardstik.com/reports?query[created_at_lt]=2021-04-01&query[created_at_gt]=2021-03-23&order_by=created_at&order=desc&page=1&per_page=20' \
  -H 'Authorization: Account [YOUR_API_KEY]' \
  -H 'Accept: application/json'

Errors

We return a number of standard HTTP errors. Below are some of the most common errors that you may run into while developing.

Status Definition
400 Bad Request
401 Unauthorized
403 Forbidden
404 Not Found
422 Unprocessable Entity
500 Internal Server Error

Below are the status codes with more information on what could have occurred.

Bad Request

The server cannot process the request. This error is most likely due to malformed request syntax.

  • code: 400
  • status: Bad Request

Bad Request

Response Schema: application/json
errors
Array of strings
application/json
{
  • "errors": [
    ]
}

Unauthorized

Similar to a 403 Forbidden, but specifically when authentication is provided and has failed, or not been provided.

This error is most likely due to not including or having a malformed API key in the request header.

  • code: 401
  • status: Unauthorized

Unauthorized

Response Schema: application/json
errors
Array of strings
application/json
{
  • "errors": [
    ]
}

Forbidden

The request is valid, but you are unable to execute the request. This error is most likely due to the API key not having the necessary permissions, or a prohibited action is attempted, such as creating a duplicate record where one already exists.

  • code: 403
  • status: Forbidden

Forbidden

Response Schema: application/json
errors
Array of strings
application/json
{
  • "errors": [
    ]
}

Not Found

The requested resource could not be found but may be available in the future. This error is often encountered when an id that does not exist is passed in a request. If you encounter this error, ensure that you are referencing the correct id and that it exists in your account.

  • code: 404
  • status: Not Found
{
  {
    "title": "Report Not Found",
    "detail": "The report was not found",
    "meta": {
      "report_id": "INVALID_REPORT_ID"
     },
    "src": {},
    "status": 404
  }
}

Unprocessable Entity

The request was well-formed, but was unable to be processed due to semantic errors. This error is most likely due to including invalid data in POST, PATCH, and PUT requests. Refer to the request documentation to ensure you are supplying the required attributes, and that the attribute types are correct.

  • code: 422
  • status: Unprocessable Entity

Unprocessable Entity

Response Schema: application/json
detail
string

Detailed message about the error.

meta
object

An object with more details about the response error.

src
object

An object containing references to the source of the error.

status
integer

Status code of the error.

title
string

A brief title of the error.

application/json
{
  • "title": "Resource not updated",
  • "detail": "There was a problem updating this resource.",
  • "meta": { },
  • "src": { },
  • "status": 422
}

Internal Server Error

An internal server error occurred due to an unexpected condition, and is most likely due to an issue with our servers. If you encounter such an error, please reach out to support@yardstik.com and we will work with you to resolve the issue.

  • code: 500

  • status: Internal Server Error

Embeddable Views

Yardstik has published a public npm library for utilizing Yardstik embeddable views https://www.npmjs.com/package/@yardstik/embeddable-sdk
You can also check out this quick start demo example of how it all works https://github.com/yardstik/embeddable-sdk-demo

Installation

The Yardstik embeddable-sdk library is available as an npm package.

  • with npm npm i @yardstik/embeddable-sdk

  • with yarn yarn add @yardstik/embeddable-sdk

Getting Started

Here is a quick example to get you started:

import { Yardstik } from '@yardstik/embeddable-sdk';

Available Views

Each available page view is a separate method on the exported Yardstik library object. The currently supported views are as follows:

  1. CandidateReportIframe: Allows the user to view and interact with a candidate report.
  2. AccountDisclosuresIframe: Allows the user to view and agree to account level agreements and terms.

Creating an Instance of an Available View

To create an instance of the desired view, which will be attached to a specified container on your page, call the applicable method and pass in the configuration object.

  const yardstikReport = new Yardstik.CandidateReportIframe(
    {
      token: myToken,
      reportId: myReportId,
      container: containerRef.current,
      domain: myDomain
    }
  );

CandidateReportIframe

The config object for the CandidateReportIframe takes the following parameters:

token: string - JWT for authorization

reportId: string - The ID of the report that you would like to review

container: HTMLElement - Container to which the iframe will be attached

domain: string - Yardstik domain that you would like to call

width?: string - Optional width of the iframe

height?: string - Optional height of the iframe

fullScreen?: boolean - Optional, if true, iframe will fill the entire page

AccountDisclosureIframe

The config object for the AccountDisclosureIframe takes the following parameters:

token: string - JWT for authorization

accountId: string - The ID of the account that you would like to review

container: HTMLElement - Container to which the iframe will be attached

domain: string - Yardstik domain that you would like to call

width?: string - Optional width of the iframe

height?: string - Optional height of the iframe

fullScreen?: boolean - Optional, if true, iframe will fill the entire page

Obtaining JWT

To obtain a JWT to include in the config object (from your backend), make a request to the JWT Route using your API token for authorization.

Shared Methods For Use By Parent Page

Each view has a shared method that can be called by the parent page and utilized for customization and performance.

destroy: When called, the destroy method will remove the iframe from its parent container. This method can be used by the parent page for clean-up.

yardstikReport.destroy()

Message Events

In order to allow the parent to take action, each iframe view is set up to post certain message events to the parent page in which it is contained.

loaded: Each iframe instance will post a 'loaded' message event when the iframe content has fully loaded. The parent page could listen for this event to trigger an action, such as rendering the iframe visible or turning off a loading animation.

  yardstikReport.on('loaded', () => {
    console.log("The iframe is ready.");
    setIframeReady(true)
  ;})

expiration: Each iframe instance will post an 'expiration' message event when the authorization token has expired. The parent can listen for this event to trigger an action, such as requesting a new JWT from your backend to refresh the session or instructing the user to refresh the page, so that a new token is generated.

  yardstikReport.on('expiration', () => {
  console.log("The JWT has expired.");
  setTokenExpired(true);
})