Class: Assinafy::Resources::SignerResource

Inherits:
BaseResource show all
Defined in:
lib/assinafy/resources/signer_resource.rb,
sig/assinafy.rbs

Overview

Signer management. Covers both:

  • Account-scoped CRUD on signers (authenticated as a workspace user).
  • Signer self-service endpoints (authenticated via signer-access-code).

See https://api.assinafy.com.br/v1/docs#signer for the full documentation of these endpoints.

Constant Summary collapse

EMAIL_REGEX =

Returns:

  • (Regexp)
/\A[^\s@]+@[^\s@]+\.[^\s@]+\z/
SIGNATURE_TYPES =

Returns:

  • (Array[String])
%w[signature initial].freeze

Constants inherited from BaseResource

BaseResource::AUTH_HEADERS, BaseResource::PAGINATION_HEADERS, BaseResource::PATH_SEGMENT

Instance Method Summary collapse

Methods inherited from BaseResource

#initialize

Constructor Details

This class inherits a constructor from Assinafy::Resources::BaseResource

Instance Method Details

#accept_terms(signer_access_code:) ⇒ nil

Accept the platform's terms of use as the signer.

The signer-access-code is sent as the documented query parameter (the signerAccessCode security scheme is in: query), consistent with every other signer-authenticated endpoint. This operation has no request body.

Examples:

Accept the terms of use

result = client.signers.accept_terms(signer_access_code: 'signer-access-code')

# Request: PUT /signers/accept-terms?signer-access-code=signer-access-code
# Body: none

# Response: { "status": 200, "message": "Terms accepted" }
# => nil

Parameters:

  • signer_access_code (String)
  • signer_access_code: (String)

Returns:

  • (nil)

    the documented success envelope has no data payload

See Also:

  • /signers/accept-terms


273
274
275
276
277
278
279
# File 'lib/assinafy/resources/signer_resource.rb', line 273

def accept_terms(signer_access_code:)
  code = require_signer_access_code(signer_access_code)

  call('Failed to accept signer terms') do
    http_put('signers/accept-terms', nil, { signer_access_code: code }, workspace_auth: false)
  end
end

#confirm_data(document_id, payload, signer_access_code:) ⇒ Hash

Confirm signer data before signing a virtual assignment. The documented body fields are full_name, email, and government_id; the payload is passed through unchanged, so any additional fields the API accepts can be supplied as well.

Examples:

Confirm the signer's data

result = client.signers.confirm_data(
  'document-id',
  { full_name: 'Signer Name', email: 'signer@example.com', government_id: '00000000000' },
  signer_access_code: 'signer-access-code'
)

# signer-access-code is sent as a query param; the JSON body the SDK sends:
#   { "full_name": "Signer Name", "email": "signer@example.com", "government_id": "00000000000" }

# => {
#   "resource" => "signer", "id" => "signer-id", "full_name" => "Signer Name",
#   "email" => "signer@example.com", "whatsapp_phone_number" => nil, "has_accepted_terms" => false
# }

Parameters:

  • document_id (String)
  • payload (Hash)

    :full_name, :email, :government_id

  • signer_access_code (String)
  • signer_access_code: (String)

Returns:

  • (Hash)

    the updated signer object (envelope data unwrapped)

See Also:

  • /documents/{documentId}/signers/confirm-data


336
337
338
339
340
341
342
343
344
345
# File 'lib/assinafy/resources/signer_resource.rb', line 336

def confirm_data(document_id, payload, signer_access_code:)
  doc_id = require_id(document_id, 'Document ID')
  body   = body_params(require_payload(payload))
  code   = require_signer_access_code(signer_access_code)

  call('Failed to confirm signer data') do
    http_put("documents/#{doc_id}/signers/confirm-data", body,
             { signer_access_code: code }, workspace_auth: false)
  end
end

#create(payload, account_id_override = nil) ⇒ Hash

Create a signer in the workspace.

Examples:

Create a signer

signer = client.signers.create(full_name: 'Example Signer', email: 'signer@example.com')

# Request body the SDK sends (nil/omitted optional fields are stripped):
#   {
#     "full_name": "Example Signer",
#     "email": "signer@example.com"
#   }

# => {
#   "resource"              => "signer",
#   "id"                    => "signer-id",
#   "full_name"             => "Example Signer",
#   "email"                 => "signer@example.com",
#   "whatsapp_phone_number" => nil,
#   "has_accepted_terms"    => false
# }

Parameters:

  • payload (Hash)
  • account_id_override (String, nil) (defaults to: nil)

Options Hash (payload):

  • :full_name (String)

    required

  • :email (String)

    optional, validated when present

  • :whatsapp_phone_number (String)

    optional

  • :phone (String)

    alias for :whatsapp_phone_number

Returns:

  • (Hash)

    signer object (envelope data unwrapped)

See Also:

  • /accounts/{account_id}/signers


63
64
65
66
67
68
69
70
71
72
# File 'lib/assinafy/resources/signer_resource.rb', line 63

def create(payload,  = nil)
  body   = validate_create!(payload)
  acc_id = ()

  @logger.info('Creating signer')

  call('Failed to create signer') do
    http_post("accounts/#{acc_id}/signers", body)
  end
end

#delete(signer_id, account_id_override = nil) ⇒ nil

Delete a signer.

Examples:

Delete a signer

client.signers.delete('signer-id')
# => nil

Parameters:

  • signer_id (String)
  • account_id_override (String, nil) (defaults to: nil)

Returns:

  • (nil)

    the SDK returns nil on success (response body is discarded)

See Also:

  • /accounts/{account_id}/signers/{signer_id}


179
180
181
182
183
184
185
186
# File 'lib/assinafy/resources/signer_resource.rb', line 179

def delete(signer_id,  = nil)
  acc_id = ()
  sid    = require_id(signer_id, 'Signer ID')

  call_void('Failed to delete signer') do
    http_delete("accounts/#{acc_id}/signers/#{sid}")
  end
end

#download_signature(signer_access_code:, type: 'signature') ⇒ String

Download the signer's signature image as raw bytes.

Examples:

Download and save the signer's signature image

png = client.signers.download_signature(
  signer_access_code: 'signer-access-code',
  type:               'signature'
)

# The SDK returns the raw response body as binary bytes (Content-Type: image/png):
# => "\x89PNG\r\n\x1A\n..." # ASCII-8BIT String
File.binwrite('signature.png', png)

Parameters:

  • signer_access_code (String)
  • type (String) (defaults to: 'signature')

    signature or initial

  • signer_access_code: (String)
  • type: (String) (defaults to: 'signature')

Returns:

  • (String)

    binary image body (ASCII-8BIT), e.g. raw PNG bytes

See Also:

  • /signature/{type}


413
414
415
416
417
418
419
420
# File 'lib/assinafy/resources/signer_resource.rb', line 413

def download_signature(signer_access_code:, type: 'signature')
  sig_type = signature_type(type)
  code     = require_signer_access_code(signer_access_code)

  call_binary('Failed to download signer signature') do
    http_get("signature/#{sig_type}", { signer_access_code: code }, workspace_auth: false)
  end
end

#find_by_email(email, account_id_override = nil) ⇒ Hash?

Convenience: find a signer by email using the documented search query parameter, then do a case-insensitive client-side match. Walks every result page (using a fixed page size; the API clamps per-page to its own maximum) until a match is found or the pages are exhausted. Returns nil when no returned signer matches. API errors still propagate.

Examples:

Find a signer by email

signer = client.signers.find_by_email('signer@example.com')

# Internally pages through GET /accounts/{account_id}/signers?search=...&per-page=50
# and returns the single matching signer Hash (case-insensitive on email):
# => {
#   "id"                    => "signer-id",
#   "full_name"             => "Example Signer",
#   "email"                 => "signer@example.com",
#   "whatsapp_phone_number" => nil,
#   "has_accepted_terms"    => false
# }
#
# => nil # when no returned signer matches

Parameters:

  • email (String)
  • account_id_override (String, nil) (defaults to: nil)

Returns:

  • (Hash, nil)

    the matching signer object, or nil when none matches



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# File 'lib/assinafy/resources/signer_resource.rb', line 211

def find_by_email(email,  = nil)
  assert_email!(email.to_s)
  target = email.to_s.downcase
  page   = 1

  loop do
    result = list({ search: email, page: page, per_page: 50 }, )
    match  = result[:data].find { |signer| signer['email'].to_s.downcase == target }
    return match if match

    meta = result[:meta]
    break unless meta && meta[:last_page] && page < meta[:last_page]

    page += 1
  end
  nil
end

#get(signer_id, account_id_override = nil) ⇒ Hash

Fetch a signer by ID.

Examples:

Fetch a signer by ID

signer = client.signers.get('signer-id')

# => {
#   "resource"              => "signer",
#   "id"                    => "signer-id",
#   "full_name"             => "Example Signer",
#   "email"                 => "signer@example.com",
#   "whatsapp_phone_number" => nil,
#   "has_accepted_terms"    => false
# }

Parameters:

  • signer_id (String)
  • account_id_override (String, nil) (defaults to: nil)

Returns:

  • (Hash)

    signer object (envelope data unwrapped)

See Also:

  • /accounts/{account_id}/signers/{signer_id}


91
92
93
94
95
96
97
98
# File 'lib/assinafy/resources/signer_resource.rb', line 91

def get(signer_id,  = nil)
  acc_id = ()
  sid    = require_id(signer_id, 'Signer ID')

  call('Failed to fetch signer') do
    http_get("accounts/#{acc_id}/signers/#{sid}")
  end
end

#list(params = {}, account_id_override = nil) ⇒ Hash{Symbol=>Array,Hash}

List signers in the workspace, with pagination metadata.

Examples:

List signers, page 1, 3 per page

result = client.signers.list(page: 1, per_page: 3)

# => {
#   data: [
#     {
#       "id"                    => "signer-id",
#       "full_name"             => "Example Signer",
#       "email"                 => "signer@example.com",
#       "whatsapp_phone_number" => nil,
#       "has_accepted_terms"    => false
#     }
#     # ... (one Hash per signer)
#   ],
#   meta: { current_page: 1, per_page: 3, total: 4, last_page: 2 }
# }

Parameters:

  • params (Hash) (defaults to: {})

    query parameters (search, page, per_page)

  • account_id_override (String, nil) (defaults to: nil)

Returns:

  • (Hash{Symbol=>Array,Hash})

    { data: [...], meta: { ... } }

See Also:

  • /accounts/{account_id}/signers


122
123
124
125
126
127
128
# File 'lib/assinafy/resources/signer_resource.rb', line 122

def list(params = {},  = nil)
  acc_id = ()

  call_list('Failed to list signers') do
    http_get("accounts/#{acc_id}/signers", params)
  end
end

#self_data(signer_access_code:) ⇒ Hash

Fetch the authenticated signer's own profile (signer-access-code auth).

Examples:

Fetch the signer's own profile

me = client.signers.self_data(signer_access_code: 'signer-access-code')

# => {
#   "resource"              => "signer",
#   "id"                    => "signer-id",
#   "full_name"             => "Signer Name",
#   "email"                 => "signer@example.com",
#   "whatsapp_phone_number" => "+15555550100",
#   "has_accepted_terms"    => false,
#   "has_signature"         => false, # self-only field
#   "has_initial"           => false, # self-only field
#   "is_signature_reusable" => false  # self-only field
# }

Parameters:

  • signer_access_code (String)
  • signer_access_code: (String)

Returns:

  • (Hash)

    signer object plus self-only fields (envelope data unwrapped)

See Also:

  • /signers/self


248
249
250
251
252
253
254
# File 'lib/assinafy/resources/signer_resource.rb', line 248

def self_data(signer_access_code:)
  code = require_signer_access_code(signer_access_code)

  call('Failed to fetch signer self') do
    http_get('signers/self', { signer_access_code: code }, workspace_auth: false)
  end
end

#update(signer_id, payload, account_id_override = nil) ⇒ Hash

Partially update a signer. Omitted fields are left unchanged. Updating email or whatsapp_phone_number is rejected while that channel is verified on an in-flight document; changing an unverified channel rotates its access and verification codes.

Examples:

Update a signer's full name and government ID

signer = client.signers.update('signer-id', full_name: 'Updated Signer',
                                             government_id: '00000000000')

# Request body the SDK sends (omitted fields are not nulled):
#   { "full_name": "Updated Signer", "government_id": "00000000000" }

# => {
#   "resource"              => "signer",
#   "id"                    => "signer-id",
#   "full_name"             => "Updated Signer",
#   "email"                 => "signer@example.com",
#   "whatsapp_phone_number" => nil,
#   "has_accepted_terms"    => false
# }

Parameters:

  • signer_id (String)
  • payload (Hash)
  • account_id_override (String, nil) (defaults to: nil)

Options Hash (payload):

  • :full_name (String)
  • :email (String)
  • :whatsapp_phone_number (String)

    E.164; normalized on save

  • :phone (String)

    alias for :whatsapp_phone_number

  • :government_id (String)

    CPF/CNPJ; digits only on save

Returns:

  • (Hash)

    updated signer object (envelope data unwrapped)

See Also:

  • /accounts/{account_id}/signers/{signer_id}


160
161
162
163
164
165
166
167
168
# File 'lib/assinafy/resources/signer_resource.rb', line 160

def update(signer_id, payload,  = nil)
  acc_id = ()
  sid    = require_id(signer_id, 'Signer ID')
  body   = signer_payload(payload, require_full_name: false, include_government_id: true)

  call('Failed to update signer') do
    http_put("accounts/#{acc_id}/signers/#{sid}", body)
  end
end

#upload_signature(content, signer_access_code:, type: 'signature', content_type: 'image/png', reuse: nil) ⇒ nil, Array

Upload the signer's signature image. The request body is raw image bytes.

Examples:

Upload a PNG signature image

bytes  = File.binread('signature.png')
client.signers.upload_signature(
  bytes,
  signer_access_code: 'signer-access-code',
  type:               'signature',
  content_type:       'image/png'
)

# The SDK sends the RAW image bytes as the body, with
# Content-Type: image/png and ?signer-access-code=...&type=signature query params.

# => nil # documented no-data envelope
# => []  # when a deployed API version returns data: []

Parameters:

  • content (String)

    raw image bytes

  • signer_access_code (String)
  • type (String) (defaults to: 'signature')

    signature or initial

  • content_type (String) (defaults to: 'image/png')

    must be image/png

  • reuse (Boolean, nil) (defaults to: nil)

    when true, marks the signature as reusable for future documents (documented reuse query flag)

Returns:

  • (nil, Array)

    nil for the OpenAPI's no-data envelope; some deployed versions return data: [], which the SDK passes through as an empty Array

See Also:

  • /signature


372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/assinafy/resources/signer_resource.rb', line 372

def upload_signature(content, signer_access_code:, type: 'signature', content_type: 'image/png', reuse: nil)
  unless content.is_a?(String) && !content.empty?
    raise ValidationError.new('Signature content must be a non-empty String')
  end

  unless content_type == 'image/png'
    raise ValidationError.new('Signature content type must be image/png')
  end

  require_boolean(reuse, 'reuse') unless reuse.nil?
  sig_type = signature_type(type)
  code     = require_signer_access_code(signer_access_code)

  call('Failed to upload signer signature') do
    @connection.post('signature') do |request|
      prepare_request(
        request,
        { signer_access_code: code, type: sig_type, reuse: reuse },
        workspace_auth: false
      )
      request.headers['Content-Type'] = content_type
      request.body = content
    end
  end
end

#validate_create!(payload) ⇒ Hash

Validate and normalize a payload accepted by #create without sending a request. Useful for preflighting multi-step workflows before they create any remote resources.

Examples:

Preflight a signer without making an API request

body = client.signers.validate_create!(
  full_name: 'Example Signer', email: 'signer@example.test'
)
# => { "full_name" => "Example Signer", "email" => "signer@example.test" }

Parameters:

  • payload (Hash)

Options Hash (payload):

  • :full_name (String)

    required

  • :email (String)

    optional, validated when present

  • :whatsapp_phone_number (String)

    optional

  • :phone (String)

    alias for :whatsapp_phone_number

Returns:

  • (Hash)

    normalized request body

Raises:



32
33
34
# File 'lib/assinafy/resources/signer_resource.rb', line 32

def validate_create!(payload)
  signer_payload(payload, require_full_name: true)
end

#verify_email(verification_code:, signer_access_code:) ⇒ nil

Verify the signer's email with a one-time verification code.

Examples:

Verify the signer's email with a one-time code

result = client.signers.verify_email(
  verification_code:  '123456',
  signer_access_code: 'signer-access-code'
)

# Request: POST /verify?signer-access-code=signer-access-code
# Body: { "verification-code": "123456" }

# Response: { "status": 200, "message": "Code verified successfully" }
# => nil

Parameters:

  • verification_code (String)
  • signer_access_code (String)
  • verification_code: (String)
  • signer_access_code: (String)

Returns:

  • (nil)

    the documented success envelope has no data payload

See Also:

  • /verify


298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/assinafy/resources/signer_resource.rb', line 298

def verify_email(verification_code:, signer_access_code:)
  verification = require_string(verification_code, 'Verification code')
  code         = require_signer_access_code(signer_access_code)

  call('Failed to verify signer email') do
    http_post(
      'verify',
      body_params(verification_code: verification),
      { signer_access_code: code },
      workspace_auth: false
    )
  end
end