Developer guide
OFAC API in Ruby: background screening jobs
Use the OFAC API in a Ruby background job with Net::HTTP. Save the screening ID and reuse the same operation key when retrying a customer check.

On this page
Screen OFAC subjects in a Ruby job
A Rails or other Ruby application can enqueue an internal operation reference after persisting the subject, coverage and idempotency key. The worker loads that record and submits the unchanged request. Keep secrets and raw identity details out of job names and routine queue logs.
If the worker restarts after the server accepted the request, recover the same operation under the documented retry rules. A corrected customer name is different: persist the correction and create a new operation with its own key. The job framework’s retry count is not an identity-review decision.
Prepare the synthetic exercise
Enqueue a saved operation for the invented sandbox subject rather than serializing identity details into the job name. Run the Ruby script with that operation key, then test a job restart. The queue entry should point to protected application storage containing the exact original request.
| Requirement | Value or action |
|---|---|
| Runtime | Use Ruby 3.1 or later with its standard JSON, Net::HTTP, URI, and OpenSSL libraries. The script is intended for a backend or trusted CLI and needs no provider SDK. |
| Client file | Save the client as screen.rb |
| Screening endpoint | POST /api/v1/screenings |
| SANCTIONSKIT_API_KEY | Sandbox key; screenings:write and results:read scopes |
| SCREENING_OPERATION_ID | Persist one operation ID; reuse only with the identical request body |
| request.json | Mira Calder: invented subject; sandbox@1: synthetic records |
{
"subject": {
"name": "Mira Calder",
"entityType": "person",
"birthDate": "1984"
},
"package": "sandbox@1",
"retention": "standard"
}Implement the Ruby request boundary
Net::HTTP supplies the transport while your job owns persistence and retries. Its network timeout cannot determine whether an SDN screening was accepted. Store the result before marking the job complete, and avoid allowing a generic job retry to generate a new Idempotency-Key.
# Ruby 3.1+. Standard library only. Backend or trusted CLI.
require 'json'
require 'net/http'
require 'uri'
require 'openssl'
begin
key = ENV.fetch('SANCTIONSKIT_API_KEY')
operation = ENV.fetch('SCREENING_OPERATION_ID')
raise 'Operation ID must not be empty.' if operation.empty?
raw = File.binread(ARGV[0] || 'request.json')
raise 'Request file exceeds 64 KiB.' if raw.bytesize > 65_536
body = JSON.parse(raw)
unless body.is_a?(Hash) && body['subject'] && (body.key?('sources') ^ body.key?('package'))
raise 'Supply a subject and exactly one of sources or package.'
end
uri = URI('https://www.sanctionskit.com/api/v1/screenings')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = "Bearer #{key}"
request['Content-Type'] = 'application/json'
request['Idempotency-Key'] = operation
request.body = JSON.generate(body)
payload = +''
status = 0
Net::HTTP.start(uri.host, uri.port, use_ssl: true, verify_mode: OpenSSL::SSL::VERIFY_PEER,
open_timeout: 10, read_timeout: 25, write_timeout: 25) do |http|
http.max_retries = 0
http.request(request) do |response|
status = response.code.to_i
response.read_body do |chunk|
raise 'Response size limit exceeded.' if payload.bytesize + chunk.bytesize > 8 * 1024 * 1024
payload << chunk
end
end
end
# Net::HTTP does not automatically follow redirects.
envelope = JSON.parse(payload)
raise "Screening incomplete: HTTP #{status}." unless (200...300).cover?(status)
data = envelope.is_a?(Hash) ? envelope['data'] : nil
unless data.is_a?(Hash) && data['id'].is_a?(String) && !data['id'].empty? &&
%w[potential_match no_match].include?(data['status']) && data['matches'].is_a?(Array)
raise 'Incomplete response: unexpected screening shape.'
end
count = data['matches'].length
if (data['status'] == 'no_match' && count != 0) || (data['status'] == 'potential_match' && count == 0)
raise 'Incomplete response: inconsistent screening shape.'
end
puts JSON.generate(id: data['id'], status: data['status'], candidateCount: count)
rescue StandardError => error
warn "Screening did not complete: #{error.class}. Check the operation before retrying."
exit 1
endRun the client
ruby screen.rb request.jsonConfirm OFAC scope before production
This Ruby job requests ofac-sdn. If the business requires non-SDN measures too, record that additional selection as part of the approved job definition. A customer correction and a source-policy change both need traceable new operations rather than a replay of the old body.
| Check | Required evidence |
|---|---|
| GET /api/v1/sources | Production key with sources:read; response data array |
| id | ofac-sdn |
| availability / fresh | available / true |
| capabilities | Contains the requested subject.entityType |
| currentVersion | Active version; retrieved_at and last_checked_at are not publication dates |
| lastImport | May describe an import that is not the active version |
| Missing or ineligible source | Inspect disabledReason; required coverage stays incomplete |
| Displayed client | Add these discovery checks before its production call |
curl --fail-with-body --max-time 25 --header "Authorization: Bearer $SANCTIONSKIT_API_KEY" "https://www.sanctionskit.com/api/v1/sources"Select ofac-sdn in the production request
| Setting | Production request |
|---|---|
| Key | Separate production credential with screenings:write |
| Coverage | sources: ["ofac-sdn"]; omit package |
| Operation ID | New ID for the changed body and environment |
| Usage | Production access and screening allowance apply |
| Subject | Only lawfully held, known identifying facts; no invented date precision |
{
"subject": {
"name": "Mira Calder",
"entityType": "person",
"birthDate": "1984"
},
"sources": [
"ofac-sdn"
],
"retention": "standard"
}Interpret OFAC evidence in this workflow
Connect data.id to the queue operation and customer reference. Use data.coverage when reopening the review, because the current source catalog may have advanced after the job ran. A candidate requires evidence review; a successful worker exit does not approve the relationship.
| Fields in data.matches[] | Review use |
|---|---|
| record.sourceId and record.id | Keep the SDN source and stable record reference attached to the candidate. |
| record.designations[].programs and legalReferences | Show the listed program and legal references; do not turn every non-SDN restriction into an asset freeze. |
| record.names, identifiers and birthDates | Compare the supplied identity with aliases and available identifiers, preserving partial-date precision. |
- data.id; data.coverage; data.versions; supplied disclaimer
- data.matches[]: record, assessment, evidence, conflicts
- GET /api/v1/results/{id}: results:read scope
- Retained evidence and case review
Verify the application behavior
| Condition | Next action |
|---|---|
| 401 / 403 | Correct credentials or scopes before retrying |
| coverage_unavailable / stale_coverage | Keep required coverage incomplete; no silent source substitution |
| Timeout after submission | Recover with the original Idempotency-Key and identical body |
| 409 | Investigate an operation-key conflict |
| 429 / 503 | Follow the specific error and Retry-After; an allowance cap may require a plan or usage change |
Contract and safe diagnostic references
| Reference | Use |
|---|---|
| error.code / error.requestId | Safe recovery diagnostics; never log the submitted subject, key or full candidate response |
| potential_match / no_match | Completed comparison outcomes; neither is an automatic business approval |
- Crash the worker after saving the result and confirm redelivery reuses the same operation.
- Change the customer name and verify that a new operation is created.
- Inspect job labels and logs for submitted names or bearer credentials.
Sources and references
Authority and technical sources
SanctionsKit documentation
Frequently asked questions
Does this Ruby OFAC job need a provider gem?
No. Ruby 3.1 or later provides the JSON, Net::HTTP, URI and OpenSSL libraries used here. Your queue framework still needs to manage the saved operation and distinguish a retry from a new customer check.
How should a Ruby job record a change from SDN-only coverage?
Save the revised source selection under a new screening operation. Keep the original SDN result with its prior scope so later reviewers can tell a broader check from recovery of an interrupted job.
How should a Ruby background job resume an interrupted OFAC check?
Load the existing job operation, request body, and idempotency key. A timeout can leave the result uncertain even if the server accepted it. Recover that operation first, then record its outcome before advancing the onboarding job.
What should a redelivered OFAC job do with an already saved result?
Reconcile the existing operation and retained result before issuing another request. Queue redelivery is not a reason to create another screening with a fresh key or charge the workflow for duplicate work.