Developer guide
Canadian sanctions API in Swift: identity review in a backend
Add Canadian autonomous sanctions checks to a Swift backend. Keep API keys on the server and link source records to your identity review.

On this page
Screen Canadian parties with a Swift backend
Keep the known legal name and relevant alternative forms in the customer record with their provenance. Unicode encoding is only one part of data quality: an accented name, a translated business label and a registered legal entity should not be silently treated as interchangeable facts.
The Swift client submits the documented subject fields to SanctionsKit’s selected Canadian source. It does not perform legal translation or determine that a commercial name identifies the same organization. Use the retained source candidate and reliable identifiers when the reviewer compares identities.
Prepare the synthetic exercise
Create a synthetic customer with an accented legal name and a separate display label, then run the Swift CLI with the legal subject. The exercise is about preserving meaning across the app and backend. UTF-8 encoding alone does not prove that two different name forms identify the same party.
| Requirement | Value or action |
|---|---|
| Runtime | Use Swift 6 with Foundation. This is a trusted CLI or server-side Swift example. Never embed a SanctionsKit API key in an iPhone, iPad, Apple TV, or macOS client distributed to end users; those apps should call your authenticated backend. |
| Client file | Save the client as Screen.swift |
| 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 Swift request boundary
Keep URLSession calls and service credentials in the trusted Swift process. Let an installed app send an authorized customer reference to your backend. Do not include candidate evidence in an error description that can escape into client telemetry or crash logs.
// Swift 6. Server or trusted CLI only. Do not ship an API key in an iOS app.
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
struct ScreeningFailure: Error { let message: String }
final class NoRedirect: NSObject, URLSessionTaskDelegate, @unchecked Sendable {
func urlSession(_ session: URLSession, task: URLSessionTask,
willPerformHTTPRedirection response: HTTPURLResponse,
newRequest request: URLRequest,
completionHandler: @escaping (URLRequest?) -> Void) { completionHandler(nil) }
}
@main struct Screen {
static func main() async {
do { try await run() }
catch {
// Do not print the request, credential, or raw response.
FileHandle.standardError.write(Data("Screening incomplete. Check the operation before retrying.\n".utf8))
exit(1)
}
}
static func run() async throws {
let env = ProcessInfo.processInfo.environment
guard let key = env["SANCTIONSKIT_API_KEY"], let operation = env["SCREENING_OPERATION_ID"], !operation.isEmpty else {
throw ScreeningFailure(message: "Missing credential or operation ID")
}
let path = CommandLine.arguments.count > 1 ? CommandLine.arguments[1] : "request.json"
let raw = try Data(contentsOf: URL(fileURLWithPath: path))
guard raw.count <= 65_536,
let body = try JSONSerialization.jsonObject(with: raw) as? [String: Any],
body["subject"] != nil, (body["sources"] != nil) != (body["package"] != nil) else {
throw ScreeningFailure(message: "Invalid request object")
}
var request = URLRequest(url: URL(string: "https://www.sanctionskit.com/api/v1/screenings")!)
request.httpMethod = "POST"; request.httpBody = raw
request.setValue("Bearer \(key)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(operation, forHTTPHeaderField: "Idempotency-Key")
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForRequest = 25; configuration.timeoutIntervalForResource = 30
let session = URLSession(configuration: configuration, delegate: NoRedirect(), delegateQueue: nil)
defer { session.invalidateAndCancel() }
// URLSession download avoids retaining an unbounded response in memory.
let (file, response) = try await session.download(for: request)
defer { try? FileManager.default.removeItem(at: file) }
let size = try file.resourceValues(forKeys: [.fileSizeKey]).fileSize ?? 0
guard size <= 8 * 1024 * 1024, let http = response as? HTTPURLResponse,
(200..<300).contains(http.statusCode) else { throw ScreeningFailure(message: "Incomplete HTTP response") }
let payload = try Data(contentsOf: file)
guard let envelope = try JSONSerialization.jsonObject(with: payload) as? [String: Any],
let data = envelope["data"] as? [String: Any], let id = data["id"] as? String, !id.isEmpty,
let status = data["status"] as? String, ["potential_match", "no_match"].contains(status),
let matches = data["matches"] as? [Any],
(status == "no_match" ? matches.isEmpty : !matches.isEmpty) else {
throw ScreeningFailure(message: "Unexpected screening response")
}
let summary: [String: Any] = ["id":id, "status":status, "candidateCount":matches.count]
FileHandle.standardOutput.write(try JSONSerialization.data(withJSONObject: summary))
FileHandle.standardOutput.write(Data("\n".utf8))
}
}Run the client
swiftc -parse-as-library Screen.swift -o screen
./screen request.jsonConfirm Canadian sanctions scope before production
The production request selects ca-autonomous for autonomous Canadian designations. Keep that scope with the backend result and the available regulation reference. Neither a translated business label nor a party-list comparison resolves every Canadian transaction restriction.
| Check | Required evidence |
|---|---|
| GET /api/v1/sources | Production key with sources:read; response data array |
| id | ca-autonomous |
| 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 ca-autonomous in the production request
| Setting | Production request |
|---|---|
| Key | Separate production credential with screenings:write |
| Coverage | sources: ["ca-autonomous"]; 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": [
"ca-autonomous"
],
"retention": "standard"
}Interpret Canadian sanctions evidence in this workflow
Use data.id to open the retained result through an authorized review path, with data.coverage and the original source names. Keep legal name, translation and trading label distinct until reliable evidence supports the relationship between them.
| Fields in data.matches[] | Review use |
|---|---|
| record.id and record.sourceId | Keep the autonomous-list record reference with the returned candidate. |
| record.designations[].legalReferences and wording | Show the available regulation and published designation context; consult the regulation when reviewing applicable restrictions. |
| record.names and identifiers | Keep bilingual or alternative names and registration details as returned, with missing evidence visibly unknown. |
- 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 |
- Round-trip an accented legal name and a separate display label.
- Verify generic Swift errors omit raw candidate evidence.
- Open retained evidence through an authorized backend route rather than exposing the service key.
Sources and references
Authority and technical sources
SanctionsKit documentation
Frequently asked questions
Does Swift Unicode support resolve Canadian name identity?
No. Encoding preserves text, while identity depends on evidence. Keep legal names, translated labels and alternative forms distinct, and compare reliable identifiers before concluding that two forms describe one organization.
Which Canadian list does the Swift production request select?
It selects ca-autonomous, the autonomous designation source. Label the result accordingly and retain any cited regulation; the request is not a substitute for reviewing all restrictions relevant to the planned activity.
Can I show full Canadian match evidence in a Swift app error message?
Keep generic failures separate from the authorized evidence view. Do not include the raw request, key, or complete candidate response in an error shown to the client or written to logs. Fetch retained evidence only through the permitted review workflow.
What belongs in a Swift error shown after Canadian screening fails?
Provide a safe application status and recovery reference. Keep the original request and candidate evidence in authorized storage, rather than embedding sensitive identity details in a client-visible error or routine log.