KycService
public final class KycService : @unchecked Sendable
Implements SEP-0012 - KYC API.
This class provides standardized endpoints for transmitting KYC and AML information to anchors and other services. It allows customers to upload their identity information and status tracking for deposit/withdrawal operations requiring identity verification.
SEP-0012 is designed to work with SEP-6 (Deposit/Withdrawal) and SEP-24 (Interactive Deposit/Withdrawal) to enable regulated on/off-ramp operations that require customer identity verification.
Typical Workflow
- Initialize Service: Create KycService from anchor’s domain
- Authenticate: Obtain JWT token using SEP-0010 WebAuthenticator
- Get Required Fields: Query which KYC fields the anchor requires
- Upload Information: Submit customer data and supporting documents
- Check Status: Monitor KYC verification status
Example: Complete KYC Flow
// Step 1: Initialize service from domain
let serviceResult = await KycService.forDomain(
domain: "https://testanchor.stellar.org"
)
guard case .success(let kycService) = serviceResult else { return }
// Step 2: Get JWT token (using SEP-0010)
let jwtToken = "..." // Obtained from WebAuthenticator
// Step 3: Check what fields are required
let getRequest = GetCustomerInfoRequest(
account: userAccountId,
jwt: jwtToken
)
let getResult = await kycService.getCustomerInfo(request: getRequest)
switch getResult {
case .success(let response):
if response.status == "NEEDS_INFO" {
// Check response.fields to see what's required
print("Required fields: \(response.fields?.keys ?? [])")
}
case .failure(let error):
print("Error: \(error)")
}
// Step 4: Upload customer information
let putRequest = PutCustomerInfoRequest(jwt: jwtToken)
putRequest.account = userAccountId
putRequest.firstName = "John"
putRequest.lastName = "Doe"
putRequest.emailAddress = "john@example.com"
putRequest.birthDate = "1990-01-01"
let putResult = await kycService.putCustomerInfo(request: putRequest)
switch putResult {
case .success(let response):
print("Customer ID: \(response.id ?? "")")
print("Status: \(response.status ?? "")")
case .failure(let error):
print("Upload failed: \(error)")
}
Example: Upload Documents
// Upload a file (e.g., photo ID)
let photoData = ... // Image data
let fileResult = await kycService.postCustomerFile(
file: photoData,
jwtToken: jwtToken
)
switch fileResult {
case .success(let response):
// Use file ID in PUT /customer request
let fileId = response.id
let putRequest = PutCustomerInfoRequest(jwt: jwtToken)
putRequest.account = userAccountId
putRequest.photoIdFrontFileId = fileId
let result = await kycService.putCustomerInfo(request: putRequest)
case .failure(let error):
print("File upload failed: \(error)")
}
Example: Verify Email/Phone
// Some anchors require verification of email or phone
// First, submit the contact info
let putRequest = PutCustomerInfoRequest(jwt: jwtToken)
putRequest.account = userAccountId
putRequest.emailAddress = "user@example.com"
await kycService.putCustomerInfo(request: putRequest)
// User receives verification code via email
// Submit the verification code
let verifyRequest = PutCustomerVerificationRequest(
id: customerId,
jwt: jwtToken
)
verifyRequest.emailAddressVerification = "123456" // Code from email
let result = await kycService.putCustomerVerification(request: verifyRequest)
Example: Check KYC Status
let getRequest = GetCustomerInfoRequest(
account: userAccountId,
jwt: jwtToken
)
let result = await kycService.getCustomerInfo(request: getRequest)
if case .success(let response) = result {
switch response.status {
case "ACCEPTED":
// KYC approved, can proceed with transactions
case "PROCESSING":
// KYC under review
case "NEEDS_INFO":
// Additional information required
case "REJECTED":
// KYC rejected
default:
break
}
}
Example: Set Status Callback
// Get notified when KYC status changes
let callbackRequest = PutCustomerCallbackRequest(
url: "https://yourapp.com/kyc-callback",
jwt: jwtToken
)
let result = await kycService.putCustomerCallback(request: callbackRequest)
Example: Delete Customer Data
// Delete all customer information (GDPR compliance)
let result = await kycService.deleteCustomerInfo(
account: userAccountId,
jwt: jwtToken
)
Error Handling
let result = await kycService.putCustomerInfo(request: putRequest)
switch result {
case .success(let response):
// Handle success
case .failure(let error):
switch error {
case .badRequest(let message):
// Invalid field values or missing required fields
case .unauthorized(let message):
// JWT token invalid or expired
case .notFound(let message):
// Customer not found
case .payloadTooLarge(let message):
// File too large
case .horizonError(let horizonError):
// Network or server error
}
}
Integration with Other SEPs
SEP-0012 is typically used alongside:
- SEP-0010: Required for authentication (JWT tokens)
- SEP-6: Non-interactive deposits/withdrawals requiring KYC
- SEP-24: Interactive deposits/withdrawals (may handle KYC in UI)
- SEP-31: Cross-border payments requiring sender/receiver KYC
Data Privacy
- Customer data is sensitive and should be transmitted securely
- Use HTTPS for all requests
- Implement proper data retention policies
- Provide data deletion functionality for GDPR compliance
- Store JWT tokens securely
See also:
- SEP-0012 Specification
- [WebAuthenticator] for SEP-0010 authentication
- [TransferServerService] for SEP-6 integration
- [InteractiveService] for SEP-24 integration
-
The base URL of the SEP-12 KYC service endpoint for customer information management.
Declaration
Swift
public let kycServiceAddress: String -
Creates a KycService instance with a direct service endpoint URL.
Declaration
Swift
public init(kycServiceAddress: String)Parameters
kycServiceAddressThe URL of the SEP-12 KYC server (e.g., “https://example.com/kyc”)
-
forDomain(domain:Asynchronous) Creates a KycService instance based on information from the stellar.toml file for a given domain.
Fetches the stellar.toml file from
{domain}/.well-known/stellar.tomland extracts the KYC_SERVER or TRANSFER_SERVER URL (KYC_SERVER takes precedence).Declaration
Swift
public static func forDomain(domain: String) async -> KycServiceForDomainEnumParameters
domainThe anchor’s domain including scheme (e.g., “https://testanchor.stellar.org”)
Return Value
KycServiceForDomainEnum with the service instance, or an error
-
getCustomerInfo(request:Asynchronous) Fetches customer information and KYC status.
This allows you to:
- Fetch the fields the server requires in order to register a new customer via a PUT /customer request
- Check the status of a customer that may already be registered
See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0012.md#customer-get
Declaration
Swift
public func getCustomerInfo(request: GetCustomerInfoRequest) async -> GetCustomerInfoResponseEnumParameters
requestGetCustomerInfoRequest containing account identifier and JWT token
Return Value
GetCustomerInfoResponseEnum with customer status and required/provided fields, or an error
-
putCustomerInfo(request:Asynchronous) Upload customer information to an anchor in an authenticated and idempotent fashion.
See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0012.md#customer-put
Declaration
Swift
public func putCustomerInfo(request: PutCustomerInfoRequest) async -> PutCustomerInfoResponseEnumParameters
requestPutCustomerInfoRequest containing customer data fields and JWT token
Return Value
PutCustomerInfoResponseEnum with customer ID and status, or an error
-
putCustomerVerification(request:Asynchronous) Submits verification codes for previously provided fields like mobile_number or email_address.
This endpoint allows servers to accept data values, usually confirmation codes, that verify a previously provided field via PUT /customer, such as mobile_number or email_address.
Declaration
Swift
public func putCustomerVerification(request: PutCustomerVerificationRequest) async -> GetCustomerInfoResponseEnumParameters
requestPutCustomerVerificationRequest containing verification codes and JWT token
Return Value
GetCustomerInfoResponseEnum with updated customer status, or an error
-
deleteCustomerInfo(account:Asynchronousjwt: ) Deletes all personal information that the anchor has stored about a given customer.
This request must be authenticated (via SEP-10) as coming from the owner of the account that will be deleted. Useful for GDPR compliance.
Declaration
Swift
public func deleteCustomerInfo(account: String, jwt: String) async -> DeleteCustomerResponseEnumParameters
accountThe Stellar account ID (G…) of the customer to delete
jwtJWT token from SEP-10 authentication
Return Value
DeleteCustomerResponseEnum indicating success or an error
-
putCustomerCallback(request:Asynchronous) Registers a callback URL to receive KYC status change notifications.
Allows the wallet to provide a callback URL to the anchor. The provided callback URL will replace (and supersede) any previously-set callback URL for this account.
See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0012.md#customer-callback-put
Declaration
Swift
public func putCustomerCallback(request: PutCustomerCallbackRequest) async -> PutCustomerCallbackResponseEnumParameters
requestPutCustomerCallbackRequest containing callback URL and JWT token
Return Value
PutCustomerCallbackResponseEnum indicating success or an error
-
postCustomerFile(file:AsynchronousjwtToken: ) Uploads a binary file (e.g., photo ID, proof of address) for use in KYC verification.
Passing binary fields such as photo_id_front or organization.photo_proof_address in PUT /customer requests must be done using the multipart/form-data content type. This endpoint is intended to decouple requests containing binary fields from requests containing nested data structures.
Once a file has been uploaded using this endpoint, its file_id can be used in subsequent PUT /customer requests. The field name for the file_id should be the appropriate SEP-9 field followed by _file_id. For example, if “file_abc” is returned as a file_id, it can be used as photo_id_front_file_id in PUT /customer.
See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0012.md#customer-files
Declaration
Swift
public func postCustomerFile(file: Data, jwtToken: String) async -> PostCustomerFileResponseEnumParameters
fileThe binary file data to upload
jwtTokenJWT token from SEP-10 authentication
Return Value
PostCustomerFileResponseEnum with file ID, or an error
-
getCustomerFiles(fileId:AsynchronouscustomerId: jwtToken: ) Retrieves information about files uploaded via postCustomerFile.
See: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0012.md#customer-files
Declaration
Swift
public func getCustomerFiles(fileId: String? = nil, customerId: String? = nil, jwtToken: String) async -> GetCustomerFilesResponseEnumParameters
fileIdOptional file ID to retrieve a specific file’s info
customerIdOptional customer ID to retrieve all files for a customer
jwtTokenJWT token from SEP-10 authentication
Return Value
GetCustomerFilesResponseEnum with file information, or an error
View on GitHub
Install in Dash