Account integration

Connect your own HR system or middleware directly to Refapp. You get one
credential and two endpoints: one to send us candidates, one to read their
status back.

This is the integration to use when the system on your side belongs to your
organisation alone. You do not need an ATS vendor in between, and Refapp does
not need to build anything specific to you.

Getting set up

Refapp enables the integration on your account. From then on a company
administrator manages it in Refapp under Company Settings → API
Configuration
, in the Account Integration section:

  • Create token issues your bearer token. It is shown once, so copy it
    then. The token both identifies your account and authenticates the call, so
    there is nothing else to send: no account id, no customer key, no second
    header.
  • API base URL is the host your calls go to. <your-refapp-host> below
    stands for it.
  • Result delivery is where you set up push, described under
    Getting results back.

Treat the token as a secret. Anyone holding it can create candidates on your
account and read their results.

Sending a candidate

POST https://<your-refapp-host>/webhooks/account-integration
Authorization: Bearer <your token>
Content-Type: application/json

The body is a partner-event object. Only four things are required:

FieldWhy it matters
candidate.idYour id for this person. See Ids below.
candidate.job.idYour id for this requisition. See Ids below.
candidate.job.titleShown to the candidate and their referees.
candidate.recruiter.emailThe person who owns this in Refapp. Invited if new.

Everything else is optional, though in practice you will want to send the
candidate's name, email and phone so we can contact them.

{
  "partner-event": {
    "candidate": {
      "id": "c-90210",
      "first-name": "Alex",
      "last-name": "Lindqvist",
      "email": "[email protected]",
      "phone": "+46701234567",
      "language": "en",
      "recruiter": {
        "email": "[email protected]",
        "name": "Sam Berg"
      },
      "job": {
        "id": "req-4471",
        "title": "Backend Engineer"
      }
    },
    "partner-result": {
      "id": "c-90210"
    }
  }
}

If you send a company block it is ignored. Your token already tells us which
account you are, and a company block in the payload has no authority over that.

Ids

Two ids do real work and must be stable.

  • candidate.job.id keys the Refapp project. Send the same value for the
    same requisition every time and candidates group under one project; send a new
    value and you get a new project.
  • candidate.id identifies the person within that requisition.

Both also appear in the URL of a push callback and in the path of a status
read, so they are how you address a candidate later. Send opaque ids from your
own system rather than anything personal: they end up in URLs, and URLs end up
in access logs on both sides.

Getting results back

Pick push, pull, or both. An account configured with neither receives
nothing at all, so choose one when you set up.

Both return exactly the same body, built by the same code, so you can move
between them, or run both, without changing your parser.

Push

Refapp PUTs results to a URL built from a pattern you set under Result
delivery
on the API Configuration page, with your own ids substituted in:

https://yourcompany.example/refapp/projects/{projectId}/candidates/{candidateId}

{projectId} becomes your job.id and {candidateId} your candidate.id.
On the same page you choose how Refapp authenticates towards your endpoint:
a static bearer token, or OAuth client credentials (client id, secret, token
URL and scope) when your endpoint issues short-lived tokens. The secret is
stored encrypted and never shown again; you can replace it at any time.

You receive a PUT when the candidate is first created, whenever the state
changes, and on completion. Answer with any 2xx. A non-2xx is treated as a
failure on our side.

Push suits you if your endpoint can accept calls from the public internet.

Pull

GET https://<your-refapp-host>/api/v1/account/projects/{projectId}/candidates/{candidateId}/results
Authorization: Bearer <your token>

The path ids are the ones you sent us. Nothing new to store.

Pull suits you if you cannot or would rather not expose an inbound endpoint.

Poll no more often than every 5 minutes. A reference check completes over
days as referees answer, so polling faster returns the same body repeatedly. If
you need results the moment they change, use push.

The result body

{
  "type": "partner-results",
  "id": "c-90210",
  "attributes": {
    "status": "pending",
    "summary": "1 of 3 references completed",
    "url": "https://app.refapp.se/candidate/abc123",
    "assessment": {
      "type": "reference-check",
      "total": 3,
      "completed": 1,
      "declined": 0,
      "remainingToSubmit": 0
    },
    "criteria": {
      "reference_check_fraud": "positive",
      "reference_check_referees_overdue": "not_overdue"
    },
    "details": {
      "Fraud": "✅ No suspected fraud",
      "Referee deadline": "✅ Not overdue"
    }
  }
}

status is one of sending, sent, pending, completed or failed.
summary is a human-readable line you can show a recruiter directly. The
optional assessment block carries the counts behind that summary.

criteria holds the values that you can route a candidate on, keyed by
criterion id. A criterion is left out until it has a value. details shows
the same values as text, keyed by criterion name. Names and labels can change.
For this reason, routing rules must use criteria, not details.

moving_criteria in the config endpoint is
the source of truth for these criteria and their values. Criteria and values
can be added or removed at any time. Refapp avoids breaking changes, so a new
criterion or value is more likely than a removed one or a changed value type.
Ignore an id or a value that you do not know.

Errors

Reads under /api/v1 answer with:

{ "error": { "code": "unauthorized", "message": "Unauthorized" } }
  • 401 if the token is missing, unknown, revoked, or the integration is not
    enabled on the account. All four answer identically on purpose, so the
    endpoint cannot be used to work out which accounts exist.
  • 404 if the project and candidate pair does not resolve within your own
    account. A candidate belonging to someone else answers exactly as one that
    does not exist anywhere, for the same reason.

The submit endpoint keeps its own response format, and also answers 401 the same
way for every kind of authentication failure.

The config endpoint, optional

GET https://<your-refapp-host>/webhooks/account-integration/config
Authorization: Bearer <your token>

Returns the settings available for your account as a list of form fields,
including which questionnaires and project templates exist. It is there if you
want to build a settings screen on your side; you can integrate perfectly well
without ever calling it.

Four query parameters shape the answer, all optional:

ParameterPurpose
job_idYour candidate.job.id. If that project already exists in Refapp, the fields reflect its current settings instead of the account defaults.
recruiterThe email of the person setting things up, so the options can be limited to what they may see, such as the questionnaires of their own unit.
langISO 639-1 code of your interface language, so labels come back localised.
webhook_dataThe current field values as a JSON string, keyed by field id. Send it when you call again after a field marked refetch changed, so dependent fields can be re-rendered.

Each field has an id, a type (header, subheader, paragraph, select,
checkbox or text), a label, and for inputs a value and any options.
A field with refetch: true asks you to fetch the config again with the
current values in webhook_data whenever it changes. The field ids are the
keys to use in webhook-data when you submit the candidate.

The response also holds moving_criteria: the criteria that your result
bodies can carry in criteria, with names and outcome labels in the lang
language. Store your rules against the criterion id and the outcome id.
These stay the same while the criterion exists, but names and labels can
change. The list is left out when your account has no criteria.

This list is the source of truth for the criteria and outcomes of your
account. Build your rules from it, not from the API reference.
The reference shows only the criteria and values that exist today.

Treat the fields and moving_criteria as dynamic content. Fields, criteria
and outcomes can be added or removed at any time. Refapp avoids breaking
changes, so a new entry is more likely than a removed one or a changed value
type. Ignore a field, criterion or outcome that you do not know.

Rotating your token

Your account can hold two active tokens at once, so you can roll over without
downtime: create a second token on the API Configuration page, move your system
across, then revoke the old one there.


Did this page help you?