Library.Workable (Portico/Z v0.1.0)

Context for accessing Workable API functions.

Requires config.exs to have a setup incantation like so:

config :library, 
  Library.Workable: [
    api_token: "yer token"
  ]

Sync with Workable

Incoming Apps

Synching with Workable is easy because they have webhooks! We've configured a webhook to the following HIVE endpoint: https://hive.explo.org/webhook/workable/candidate/hired

Whenever a candidate is hired on Workable, a HIVE atom {workable, candidate, hired}, will be created, and the data posted will be in this format.

Manual API Queries

Workable can also be queried directly from the Z repository via this module. This module is where we configured the webhook (subscriptions) endpoint, but you can also use it to list jobs and candidates, as well as getting more candidate detail on a per-candidate (ID) basis. This was how we created the initial list of candidates who had been hired prior to configuring the webhook.

# create the one subscription we need (this should only really happen once)
endpoint = "https://hive.explo.org/webhook/workable/participant/hired?token=TOKENHERE"
{:ok, response} = Z.External.Workable.post_subscription %{target: endpoint, event: "candidate_moved", args: %{account_id: "explo", stage: "hired"}}

A Word on API Formats

The /candidate/:id format and the webhook:candidate_moved format are very similar but not the same. Basically, the main JSON key is different (candidate versus data) and the webhook doesn't contain the full candidate picture.

Accordingly, the method to convert a candidate to a HIVE atom candidate_to_hive_atom/1 is to swap the keys. Then, on receipt, we hit the candidate endpoint with candidate ID and use that data. See ZWeb.API.Staff.ApplicationsController for more details there.

Link to this section Summary

Functions

Get a candidate by ID and convert it to a valid HIVE atom.

Get a Workable candidate by ID.

List all candidates on Workable.

List all posted jobs on Workable.

List all webhook subscriptions from Workable.

Create a subscription aka webhook in Workable.

Link to this section Functions

Link to this function

candidate_to_hive_atom(id)

Specs

candidate_to_hive_atom(String.t()) :: HiveAtom.t()

Get a candidate by ID and convert it to a valid HIVE atom.

The atom should conform with the spec defined here: https://3.basecamp.com/3519819/buckets/18887972/documents/3541228600

Link to this function

get_candidate(id)

Specs

get_candidate(String.t()) :: Tesla.Env.Result.t()

Get a Workable candidate by ID.

See https://workable.readme.io/docs/job-candidates-create for more information about the returned data.

Link to this function

list_candidates(options \\ [])

Specs

list_candidates(Keyword.t()) :: Tesla.Env.Result.t()

List all candidates on Workable.

Valid query options can be found here: https://workable.readme.io/docs/job-candidates-index

One option of note is stage: "hired", which is basically all that we use.

Link to this function

list_jobs(options \\ [])

Specs

list_jobs(Keyword.t()) :: Tesla.Env.Result.t()

List all posted jobs on Workable.

Valid query options can be found here: https://workable.readme.io/docs/jobs

Link to this function

list_subscriptions()

Specs

list_subscriptions() :: Tesla.Env.Result.t()

List all webhook subscriptions from Workable.

There really should be just one - for when staff get hired. See here for more detail.

Examples

iex> subscriptions = Library.Workable.list_subscriptions()
iex> Enum.at(subscriptions, 0)["event"]
"candidate_moved"
Link to this function

post_subscription(options \\ %{})

Specs

post_subscription(map()) :: Tesla.Env.Result.t()

Create a subscription aka webhook in Workable.

This really should only ever happen once, and it should have already happened. See here for more detail.

Valid query options can be found here: https://workable.readme.io/docs/webhooks

Some important options

  • target (String) - target webhook endpoint
  • event (String) - "candidate_created" or "candidate_moved"
  • args (Map) - %{account_id: "explo", stage: "OPTIONAL", job_shortcode: "OPTIONAL"}