Payability internal services

Three clients talk to Payability’s own platform. All three authenticate with PybApiAuth (pyb-sdk), which exchanges the Basic PAYABILITY_AUTH_TOKEN for an X-Session-Token and attaches it to every request — no separate credential is needed.

components/payability_internal_api.py

Base URL PAYABILITY_INTERNAL_API_URL (https://api.payability.com/v1/mp in release, https://<env>.dev.payability.com/database_v2 in dev).

Method Call

get_supplier_info(supplier_key, detailed=False)

GET /internal/suppliers/{supplier_key} (?detailed=1). detailed includes owners, each with loginKey, firstName, lastName — the source for name matching and Onfido applicant creation.

get_user_perms(login_key)

GET /internal/logins/{login_key}/perms. Wrapped in @lru_cache(1), so within one process only the most recent login_key is cached.

upload_supplier_documents(supplier_key, files, document_type, compliance_key, description=None, sides=None, expiration_dates=None, file_document_types=None, file_issuing_countries=None)

POST /sup_widget/suppliers/{supplier_key}/documents as multipart/form-data with a repeatable files part. Returns the created document record(s), each with an id.

list_supplier_documents(supplier_key, document_type)

GET /internal/suppliers/{supplier_key}/documents/{document_type}. Each record carries an id and a nested compliance object whose status (pending, approved, archived) drives the archive decision.

update_supplier_document_statuses(supplier_key, document_ids, status)

PATCH /internal/suppliers/{supplier_key}/documents with [{"id": …, "status": …}, …]. Used to archive stale captures and to approve freshly uploaded ones; the internal API cascades the "Upload ID" compliance status once all identity documents are approved.

Per-file metadata on upload

document_type and compliance_key are single values applied to the whole call. sides, expiration_dates, file_document_types and file_issuing_countries are positional arrays emitted as repeated form fields — one entry per files part, in order, empty string where a file has no value — so the server can align them with the uploaded parts. The form field names differ slightly from the Python argument names:

Argument Form field

sides

side

expiration_dates

expiration_date

file_document_types

document_types

file_issuing_countries

issuing_country

The data payload is built as an ordered list of tuples rather than a dict, precisely so those keys can repeat.

Error bodies are preserved

The three document methods go through _raise_with_body, which raises requests.HTTPError with a truncated copy of the upstream body in the message:

def _raise_with_body(response: requests.Response):
    if response.ok:
        return

    body = (response.text or '')[:1000]
    raise requests.HTTPError(
        '{status} {reason} for url: {url} - response body: {body}'.format(...),
        response=response
    )

A bare raise_for_status() would discard the rejection reason (an invalid compliance_key, an unaccepted file, an unknown document id), which is what makes 4xx responses debuggable in the worker logs and in the SWF failure details.

get_supplier_info and get_user_perms still use plain raise_for_status().

components/pyb_documents.py

Fetches previously uploaded identity-document images from the PYB Documents service (PYB_DOCUMENTS_URL).

def get_document(self, document_identifier: str) -> bytes:
    response = self.__session.get(
        url='{}/documents/{}'.format(settings.PYB_DOCUMENTS_URL, document_identifier)
    )

    if response.status_code == 404:
        raise DocumentNotFound(document_identifier)

    response.raise_for_status()

    return response.content

Used by the Au10tix handle task to obtain the image it submits for verification.

components/b2b_task_manager.py

Starts SWF workflows through the shared B2B Task Manager HTTP service (B2B_TASK_MANAGER_URL) — the Web API never calls SWF directly.

  • start_swf(domain, workflow_type, workflow_type_version, workflow_identifier, input_body) → POST /executeWorkflow, injecting gci from B2B_TASK_MANAGER_GCI and taskList=default.

  • A 2xx response carrying errorMessages or exceptionMessages raises CannotRunWorkflow, which renders WorkflowExecutionAlreadyStarted as "Task already running". Because it is a TaskExecutionError, task_failure_handling re-raises it untouched and the API answers 400 (or 409 for the Entrust reprocess path).

Full detail: SWF orchestration.

Who uses what

Consumer Internal calls

Au10tix handle task

PYBDocuments.get_document, get_supplier_info(detailed=True) for name matching.

Entrust session endpoint

get_supplier_info(detailed=True) for the owner’s name.

Entrust aftermath task

list_supplier_documents, update_supplier_document_statuses (archive, then approve), upload_supplier_documents (twice: documents then liveness media).

Permissions

get_user_perms.

Every request task, the Entrust webhook, the Entrust reprocess path

B2BTaskManager.start_swf.

The document upload route expects the PermCode.INT_LOGIN carried by the session token that PybApiAuth obtains, which is why no extra secret is involved.

Tests

development/unit_tests/components_/test_payability_internal_api.py, development/unit_tests/components_/test_swf_client.py.