DocFox exposes the nCino KYC DocFox API endpoints through Active Call service objects.
Install the gem and add to the application's Gemfile by executing:
bundle add active_call-doc_fox
If bundler is not being used to manage dependencies, install the gem by executing:
gem install active_call-doc_fox
Configure your API credentials.
In a Rails application, the standard practice is to place this code in a file named doc_fox.rb
within the config/initializers
directory.
require 'active_call-doc_fox'
DocFox::BaseService.configure do |config|
config.api_key = ''
config.secret = ''
# Optional configuration.
config.cache = Rails.cache # Default: ActiveSupport::Cache::MemoryStore.new
config.logger = Rails.logger # Default: Logger.new($stdout)
config.logger_level = :debug # Default: :info
config.log_headers = true # Default: false
config.log_bodies = true # Default: false
end
Each service object returned will undergo validation before the call
method is invoked to access API endpoints.
After successful validation.
service.success? # => true
service.errors # => #<ActiveModel::Errors []>
After failed validation.
service.success? # => false
service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=id, type=blank, options={}>]>
service.errors.full_messages # => ["Id can't be blank"]
After a successful call
invocation, the response
attribute will contain a Faraday::Response
object.
service.success? # => true
service.response # => #<Faraday::Response ...>
service.response.success? # => true
service.response.status # => 200
service.response.body # => {"data"=>{"id"=>"aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb", "type"=>"kyc_application", "attributes"=>{}}, ...}
At this point you will also have a facade
object which will hold all the attributes for the specific resource.
service.facade # => #<DocFox::KycApplication::Facade @kyc_entity_type_name="South African Citizen" ...>
service.facade.kyc_entity_type_name # => 'South African Citizen'
For convenience, facade attributes can be accessed directly on the service object.
service.kyc_entity_type_name # => 'South African Citizen'
After a failed call
invocation, the response
attribute will still contain a Faraday::Response
object.
service.success? # => false
service.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=base, type=not_found, options={}>]>
service.errors.full_messages # => ["Not Found"]
service.response # => #<Faraday::Response ...>
service.response.success? # => false
service.response.status # => 404
service.response.body # => {"errors"=>[{"title"=>"not_found", "status"=>"404", "detail"=>"The resource you requested could not be found"}]}
Each service object returned will undergo validation before the call!
method is invoked to access API endpoints.
After successful validation.
service.success? # => true
After failed validation, a DocFox::ValidationError
exception will be raised with an errors
attribute which
will contain an ActiveModel::Errors
object.
rescue DocFox::ValidationError => exception
exception.message # => ''
exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=id, type=blank, options={}>]>
exception.errors.full_messages # => ["Id can't be blank"]
After a successful call!
invocation, the response
attribute will contain a Faraday::Response
object.
service.success? # => true
service.response # => #<Faraday::Response ...>
service.response.success? # => true
service.response.status # => 200
service.response.body # => {"data"=>{"id"=>"aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb", "type"=>"kyc_application", "attributes"=>{}}, ...}
At this point you will also have a facade
object which will hold all the attributes for the specific resource.
service.facade # => #<DocFox::KycApplication::Facade @kyc_entity_type_name="South African Citizen" ...>
service.facade.kyc_entity_type_name # => 'South African Citizen'
For convenience, facade attributes can be accessed directly on the service object.
service.kyc_entity_type_name # => 'South African Citizen'
After a failed call!
invocation, a DocFox::RequestError
will be raised with a response
attribute which will contain a Faraday::Response
object.
rescue DocFox::RequestError => exception
exception.message # => 'Not Found'
exception.errors # => #<ActiveModel::Errors [#<ActiveModel::Error attribute=base, type=not_found, options={}>]>
exception.errors.full_messages # => ["Not Found"]
exception.response # => #<Faraday::Response ...>
exception.response.status # => 404
exception.response.body # => {"errors"=>[{"title"=>"not_found", "status"=>"404", "detail"=>"The resource you requested could not be found"}]}
An example of where to use call
would be in a controller doing an inline synchronous request.
class SomeController < ApplicationController
def update
@service = DocFox::KycApplication::UpdateService.call(**params)
if @service.success?
redirect_to [@service], notice: 'Success', status: :see_other
else
flash.now[:alert] = @service.errors.full_messages.to_sentence
render :edit, status: :unprocessable_entity
end
end
end
An example of where to use call!
would be in a job doing an asynchronous request.
You can use the exceptions to determine which retry strategy to use and which to discard.
class SomeJob < ApplicationJob
discard_on DocFox::NotFoundError
retry_on DocFox::RequestTimeoutError, wait: 5.minutes, attempts: :unlimited
retry_on DocFox::TooManyRequestsError, wait: :polynomially_longer, attempts: 10
def perform
DocFox::KycApplication::UpdateService.call!(**params)
end
end
If you don't provide the per_page
argument, multiple API requests will be made untill all records have been returned. You could be rate limited, so use wisely.
KYC Applications
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications/get
DocFox::KycApplication::ListService.call(page: 1, per_page: 10).each do |facade|
facade.id
end
Filter by names, numbers contact information or external refs.
DocFox::KycApplication::ListService.call(search_term: '[email protected]').map { _1 }
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D/get
service = DocFox::KycApplication::GetService.call(id: '')
service.id
service.approved_at
service.archived
service.created_at
service.status
service.relationships.dig('profile', 'data', 'id')
service.relationships.dig('data_requirements', 'links', 'related')
...
Include related resources.
managed_by
, profile.additional_details
, profile.addresses
, profile.contacts
, profile.names
, profile.numbers
, parent_related_entities
, onboarding_token_summary
, onboarding_token_summary.status_summaries
, onboarding_token_summary.data_requirement_summaries
, onboarding_token_summary.kyc_entity_template_designation_schema
, onboarding_token_summary.parent_related_entity_designation_summary
, onboarding_token_summary.parent_onboarding_token_summary
, onboarding_token_summary.relationship_builder_config
service = DocFox::KycApplication::GetService.call(id: '', params: { include: 'managed_by,profile.names' })
service.included
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications/post
DocFox::KycApplication::CreateService.call(
data: {
type: 'kyc_application',
attributes: {
kyc_entity_template_id: 'aaaaaaaa-1111-2222-3333-bbbbbbbbbbbb',
names: {
first_names: 'Eric Theodore',
last_names: 'Cartman'
},
# ...more fields following the schema definitions.
}
}
)
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D~1approve/patch
DocFox::KycApplication::ApproveService.call(id: '')
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D~1unapprove/patch
DocFox::KycApplication::UnapproveService.call(id: '', reason: '')
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D~1transfer/patch
DocFox::KycApplication::TransferService.call(id: '', transfer_to_user_id: '')
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D~1archive/post
DocFox::KycApplication::ArchiveService.call(id: '', reason: '')
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D~1unarchive/post
DocFox::KycApplication::UnarchiveService.call(id: '', reason: '')
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Applications/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D/delete
DocFox::KycApplication::DeleteService.call(id: '')
Status Summaries
# https://www.docfoxapp.com/api/v2/documentation#tag/Status-Summaries/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D~1status_summaries/get
service = DocFox::StatusSummary::GetService.call(id: '')
service.id
service.overall_kyc_application_status
service.module_statuses
...
Profiles
# https://www.docfoxapp.com/api/v2/documentation#tag/Profiles/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D~1profiles/get
DocFox::Profile::ListService.call(kyc_application_id: '', page: 1, per_page: 10).each do |facade|
facade.id
end
# https://www.docfoxapp.com/api/v2/documentation#tag/Profiles/paths/~1api~1v2~1profiles~1%7Bprofile_id%7D/get
service = DocFox::Profile::GetService.call(id: '')
service.id
service.created_at
...
Include related resources.
additional_details
, addresses
, contacts
, contacts.invitation_tokens
, names
, numbers
service = DocFox::Profile::GetService.call(id: '', params: { include: 'names,numbers,additional_details' })
service.included
Data Requirements
# https://www.docfoxapp.com/api/v2/documentation#tag/Data-Requirements/paths/~1api~1v2~1kyc_applications~1%7Bkyc_application_id%7D~1data_requirements/get
DocFox::DataRequirement::ListService.call(kyc_application_id: '', page: 1, per_page: 10).each do |facade|
facade.id
end
List only data requirements for forms.
DocFox::DataRequirement::ListService.call(kyc_application_id: '', forms: true).map { _1 }
# https://www.docfoxapp.com/api/v2/documentation#tag/Data-Requirements/paths/~1api~1v2~1account_applications~1%7Baccount_application_id%7D~1data_requirements/get
DocFox::DataRequirement::ListService.call(account_application_id: '', page: 1, per_page: 10).each do |facade|
facade.id
end
List only data requirements for forms.
DocFox::DataRequirement::ListService.call(account_application_id: '', forms: true).map { _1 }
# https://www.docfoxapp.com/api/v2/documentation#tag/Data-Requirements/paths/~1api~1v2~1data_requirements~1%7Bdata_requirement_id%7D/get
service = DocFox::DataRequirement::GetService.call(id: '')
service.id
service.created_at
service.form
service.name
service.required
service.slug
service.valid_evidence_types
...
Include related resources.
evidence_submissions
, active_evidence_submission
, active_evidence_submission.form
, active_evidence_submission.form.active_form_datum
service = DocFox::DataRequirement::GetService.call(id: '', params: { include: 'evidence_submissions,active_evidence_submission' })
service.included
KYC Entity Templates
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Entity-Templates/paths/~1api~1v2~1kyc_entity_templates/get
DocFox::KycEntityTemplate::ListService.call(page: 1, per_page: 10).each do |facade|
facade.id
end
# https://www.docfoxapp.com/api/v2/documentation#tag/KYC-Entity-Templates/paths/~1api~1v2~1kyc_entity_templates~1%7Bkyc_entity_template_id%7D/get
service = DocFox::KycEntityTemplate::GetService.call(id: '')
service.id
service.kyc_entity_type_name
service.kyc_entity_type_category
service.created_at
service.profile_schema
...
Evidence Submissions
# https://www.docfoxapp.com/api/v2/documentation#tag/Evidence-Submissions/paths/~1api~1v2~1data_requirements~1%7Bdata_requirement_id%7D~1evidence_submissions/get
DocFox::EvidenceSubmission::ListService.call(data_requirement_id: '', page: 1, per_page: 10).each do |facade|
facade.id
end
# https://www.docfoxapp.com/api/v2/documentation#tag/Evidence-Submissions/paths/~1api~1v2~1evidence_submissions~1%7Bevidence_submission_id%7D/get
service = DocFox::EvidenceSubmission::GetService.call(id: '')
service.id
service.evidence_type
service.form
service.status
service.created_at
service.attributes
...
Include related resources.
document
: the related document, for document evidence submission
qualitative_checks
: the related qualitative checks, for document evidence submission
rejection_reasons
: the related rejection reasons
form
: the related form, for form evidence submission
form.active_form_datum
: the related form and its latest data submission, for form evidence submission
service = DocFox::EvidenceSubmission::GetService.call(id: '', params: { include: 'document,qualitative_checks' })
service.included
At the moment this is used only to add third party data to an external evidence submission for a specific third party provider.
# https://www.docfoxapp.com/api/v2/documentation#tag/Evidence-Submissions/paths/~1api~1v2~1evidence_submissions~1%7Bevidence_submission_id%7D/patch
DocFox::EvidenceSubmission::UpdateService.call(
id: '',
data: {
type: 'evidence_submission',
id: '',
attributes: {
results: {
dob: '1984-01-01',
gender: 'M',
surname: 'Cartman',
forename1: 'Eric',
forename2: nil,
id_number: '8401017223183',
addresses: {
address: [
{
addr_type: 'R',
addr_line1: '28201 E. Bonanza St.',
addr_line2: '',
addr_line3: '',
addr_line4: 'South Park',
addr_postal_code: '8000',
addr_update_date: '2017-11-21'
}
]
},
telephones: {
telephone: [
{
tel_num: '27715555555',
tel_type: 'Cell',
tel_update_date: '2017-09-05'
}
]
},
deceased_date: nil,
deceased_flag: 'N',
verified_date: '2015-06-23',
verified_flag: 'Y',
deceased_reason: nil
}
}
}
)
# https://www.docfoxapp.com/api/v2/documentation#tag/Evidence-Submissions/paths/~1api~1v2~1evidence_submissions~1%7Bevidence_submission_id%7D~1reject/patch
DocFox::EvidenceSubmission::RejectService.call(id: '', reason: '')
# https://www.docfoxapp.com/api/v2/documentation#tag/Evidence-Submissions/paths/~1api~1v2~1evidence_submissions~1%7Bevidence_submission_id%7D~1approve/patch
DocFox::EvidenceSubmission::ApproveService.call(id: '')
# https://www.docfoxapp.com/api/v2/documentation#tag/Evidence-Submissions/paths/~1api~1v2~1evidence_submissions~1%7Bevidence_submission_id%7D~1replace/patch
DocFox::EvidenceSubmission::ReplaceService.call(id: '')
Documents
# https://www.docfoxapp.com/api/v2/documentation#tag/Documents/paths/~1api~1v2~1evidence_submissions~1%7Bevidence_submission_id%7D~1documents/get
DocFox::Document::ListService.call(evidence_submission_id: '', page: 1, per_page: 10).each do |facade|
facade.id
end
# https://www.docfoxapp.com/api/v2/documentation#tag/Documents/paths/~1api~1v2~1documents~1%7Bdocument_id%7D/get
service = DocFox::Document::GetService.call(id: '')
service.id
service.created_at
service.content_type
service.filename
service.token
service.token_expiry
service.uploaded_by
...
Include related resources.
evidence_submissions
service = DocFox::Document::GetService.call(id: '', params: { include: 'evidence_submissions' })
service.included
Document Tokens
# https://www.docfoxapp.com/api/v2/documentation#tag/Document-Tokens/paths/~1api~1v2~1documents~1%7Bdocument_id%7D~1document_tokens/get
DocFox::DocumentToken::ListService.call(document_id: '', page: 1, per_page: 10).each do |facade|
facade.id
end
# https://www.docfoxapp.com/api/v2/documentation#tag/Document-Tokens/paths/~1api~1v2~1document_tokens~1%7Bdocument_token_id%7D/get
service = DocFox::DocumentToken::GetService.call(id: '')
service.id
service.created_at
service.expiry
service.content_type
...
# https://www.docfoxapp.com/api/v2/documentation#tag/Document-Tokens/paths/~1api~1v2~1documents~1%7Bdocument_id%7D~1document_tokens/post
DocFox::DocumentToken::CreateService.call(
document_id: '',
data: {
type: 'document_token'
}
)
Download Documents
# https://www.docfoxapp.com/api/v2/documentation#tag/Download-Documents/paths/~1api~1v2~1document_file_downloads~1%7Bdocument_token_id%7D/get
service = DocFox::Document::DownloadService.call(document_token_id: '', content_type: 'image/jpeg')
service.response.body # => "\xFF\xD8\xFF\xDB\x00\x84\x00\x04\x05\x..."
Possible values for content_type
.
image/jpeg
image/png
application/pdf
You can also provide just the document_id
and the service will automatically request the document_token_id
and content_type
for you.
service = DocFox::Document::DownloadService.call(document_id: '')
service.response.body # => "\xFF\xD8\xFF\xDB\x00\x84\x00\x04\x05\x..."
Document Uploads
# https://www.docfoxapp.com/api/v2/documentation#tag/Document-Uploads/paths/~1api~1v2~1evidence_submissions~1%7Bevidence_submission_id%7D~1documents/post
DocFox::Document::UploadService.call(
evidence_submission_id: '',
data: {
type: 'document',
attributes: {
evidence_type: 'taxes_paid_in_prior_quarter',
filename: 'taxes_paid.png',
document_data: "data:image/png;base64,#{Base64.encode64(File.read('/path/to/taxes_paid.png'))}"
}
}
)
Users
# https://www.docfoxapp.com/api/v2/documentation#tag/Users/paths/~1api~1v2~1users/get
DocFox::User::ListService.call(page: 1, per_page: 10).each do |facade|
facade.id
end
# https://www.docfoxapp.com/api/v2/documentation#tag/Users/paths/~1api~1v2~1users~1%7Buser_id%7D/get
service = DocFox::User::GetService.call(id: '')
service.id
service.email
service.first_names
service.last_names
service.deactivated
...
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Bug reports and pull requests are welcome on GitHub at https://github.com/kobusjoubert/doc_fox.
The gem is available as open source under the terms of the MIT License.