Skip to content

Credit Line Requests API

All endpoints require a valid admin JWT token unless noted otherwise. Routes are prefixed per section below.

Authentication

Authorization: Bearer <token_admin>

Role-based access is enforced via permissionMiddleware.canByName. Permissions required per endpoint are listed in the Permission column.

PermissionGrants
credit-line-requestsRead-only access (list, detail, downloads)
credit-line-requests-manageMutating actions: status, details, document file upload, pagaré format upload
credit-line-requests-review-documentsDocument review: approve/reject documents (single + bulk) and validate the signed pagaré
credit-line-requests-create-docusign-contractCreate and send the DocuSign envelope

Credit Line Requests (/credit-line-requests)

Endpoint Summary

MethodPathDescriptionAuthPermission
GET/credit-line-requestsPaginated list with filters and CSV exporttoken_admincredit-line-requests
GET/credit-line-requests/{id}Request detail with enriched documentstoken_admincredit-line-requests
GET/credit-line-requests/{id}/credit-line-historyHistory of credit line amounts for the companytoken_admincredit-line-requests
PATCH/credit-line-requests/{id}/statusApprove (commercial) or reject a requesttoken_admincredit-line-requests-manage
PATCH/credit-line-requests/{id}/detailsUpdate approved amount, days, credit line typetoken_admincredit-line-requests-manage
PATCH/credit-line-requests/{id}/documents/{documentId}Approve or reject a single documenttoken_admincredit-line-requests-review-documents
PATCH/credit-line-requests/{id}/documents/bulkBulk approve or reject all documentstoken_admincredit-line-requests-review-documents
PUT/credit-line-requests/{id}/documents/{documentId}/fileUpload a file for a document slottoken_admincredit-line-requests-manage
POST/credit-line-requests/{id}/pagare/formatUpload the pagaré format for the client to sign (Mexico)token_admincredit-line-requests-manage
PATCH/credit-line-requests/{id}/pagare/validateValidate (approve/reject) the client's signed pagaré (Mexico)token_admincredit-line-requests-review-documents
GET/credit-line-requests/{id}/documents/downloadDownload all documents as ZIPtoken_admincredit-line-requests
POST/credit-line-requests/{id}/contractCreate and send DocuSign envelopetoken_admincredit-line-requests-create-docusign-contract
GET/credit-line-requests/{id}/contract/signed-downloadStream signed contract PDF from S3token_admincredit-line-requests
POST/credit-line-requests/{id}/contract/signed-storage-syncRe-download signed PDF from DocuSign and persist to S3token_admincredit-line-requests
POST/credit-line-requests/webhooks/docusignDocuSign Connect callback (no JWT; HMAC-secured)None

GET /credit-line-requests

Returns a paginated list of credit line requests. When report=true, streams a CSV export.

Query Parameters:

ParamTypeRequiredDescription
startnumberNoOffset (default: 0)
lengthnumberNoPage size (default: 25)
sortBystringNoColumn to sort by
sortTypestringNoasc or desc
statusstringNoFilter by request status
contractStatusstringNoFilter by contract status
companynumberNoFilter by company ID
ticketIdnumberNoFilter by ticket ID
startDatestringNoCreation date range start (YYYY-MM-DD)
endDatestringNoCreation date range end (YYYY-MM-DD)
localeIdanyNoFilter by locale (injected by filterLocaleV2 middleware)
reportbooleanNoWhen true, streams a CSV export instead of JSON

Response (200):

json
{
  "draw": 25,
  "recordsTotal": 120,
  "recordsFiltered": 34,
  "data": [
    {
      "id": 42,
      "ticket_id": 1001,
      "company_id": 55,
      "company_name": "Acme Corp",
      "country_code": "MX",
      "assigned_to_name": "Legal Executive Name",
      "requested_amount": 500000,
      "requested_days": 30,
      "approved_amount": null,
      "approved_days": null,
      "status": "pending_commercial",
      "contract_status": "no_contract",
      "sla_days": 3,
      "created_at": "2026-04-01T10:00:00Z"
    }
  ]
}

GET /credit-line-requests/{id}

Returns a single request with all submitted documents enriched with document_name and document_type resolved from catalog_credit_line_variables via the generic_forms field map.

Response (200):

json
{
  "data": {
    "id": 42,
    "company_id": 55,
    "ticket_id": 1001,
    "status": "approved_commercial",
    "contract_status": "sent",
    "docusign_envelope_id": "abc123-...",
    "requested_amount": 500000,
    "requested_days": 30,
    "approved_amount": 300000,
    "approved_days": 30,
    "rejection_reason": null,
    "requires_pagare": true,
    "documents": [
      {
        "id": 10,
        "document_name": "RFC Certificate",
        "document_type": "file",
        "value": null,
        "status": "approved",
        "required": true,
        "rejection_reason": null
      },
      {
        "id": 31,
        "field_id": "pagare_format",
        "document_type": "file",
        "value": "https://s3.amazonaws.com/envia/credit-line-requests/42/pagare/...",
        "status": "approved"
      },
      {
        "id": 32,
        "field_id": "pagare_signed",
        "document_type": "file",
        "value": "https://s3.amazonaws.com/envia/...",
        "status": "approved"
      }
    ]
  }
}

requires_pagare (boolean) tells the frontend whether to render the pagaré sub-flow. The pagare_format/pagare_signed documents are returned in the same documents array but are surfaced in a dedicated UI section rather than the generic document list.


PATCH /credit-line-requests/{id}/status

Advances or terminates the request lifecycle.

Payload:

json
{
  "status": "approved_commercial",
  "rejection_reason": null
}
FieldTypeRequiredDescription
statusstringYesapproved_commercial or rejected
rejection_reasonstringNoRequired when status = rejected

Response: 204 No Content


PATCH /credit-line-requests/{id}/details

Updates the commercial team's decisions (approved amount, term, credit type).

Payload (at least one field required):

json
{
  "approved_amount": 300000,
  "approved_days": 30,
  "credit_line_type": 1
}

Response: 204 No Content


PATCH /credit-line-requests/{id}/documents/{documentId}

Approves or rejects a single submitted document.

Payload:

json
{
  "status": "rejected",
  "rejection_reason": "Document is illegible"
}
FieldTypeRequiredDescription
statusstringYesapproved or rejected
rejection_reasonstringConditionalRequired when status = rejected

Response: 204 No Content


PATCH /credit-line-requests/{id}/documents/bulk

Applies the same status to all documents on the request in one call.

Payload:

json
{
  "status": "approved",
  "rejection_reason": null
}

Response: 204 No Content


PUT /credit-line-requests/{id}/documents/{documentId}/file

Uploads a binary file for a document slot. Accepts multipart/form-data.

Payload: file field (max 10 MB).

Response: 204 No Content


POST /credit-line-requests/{id}/pagare/format

Mexico-only sub-flow. Legal uploads the pagaré format (the unsigned promissory note) for the client to download and sign. Stores it as the pagare_format document and moves the request to pagare_sent. A system comment with signing instructions (Monterrey legal office) is added to the linked ticket.

Accepts multipart/form-data.

Payload: file field (max 10 MB; PDF/DOC/DOCX).

Preconditions (validated server-side):

  • Request status is approved_commercial
  • The company's country requires a pagaré (an active credit_line_request_pagare form exists)
  • All required KYC/KYB documents are approved

Response (200):

json
{ "data": { "status": "pagare_sent" } }

Errors:

StatusWhen
404Credit line request not found
400Request not in approved_commercial status
400Pagaré flow not enabled for the company's country
400Required documents not all approved (response includes pending_documents)

PATCH /credit-line-requests/{id}/pagare/validate

Mexico-only sub-flow. Legal validates the client's signed pagaré. The request must be in pagare_review and a pagare_signed document must exist.

Payload:

json
{ "status": "approved" }
FieldTypeRequiredDescription
statusstringYesapproved or rejected

Behavior:

  • approved → marks pagare_signed approved and moves the request to pagare_validated (ready to create the DocuSign contract).
  • rejected → deletes the signed pagaré file (S3) and its row, returns the request to pagare_sent so the client re-uploads.

Response (200):

json
{ "data": { "status": "pagare_validated" } }

Errors:

StatusWhen
404Credit line request not found
400Request not in pagare_review status
400No signed pagaré has been uploaded

POST /credit-line-requests/{id}/contract

Validates contract preconditions and creates a DocuSign envelope from the registered template for the company's locale and person type.

Preconditions (validated server-side):

  • Request status is approved_commercial or pagare_validated
  • All required documents are approved
  • For pagaré countries (Mexico): the signed pagaré (pagare_signed) is approved
  • No existing DocuSign envelope

On success the request moves to contract_sent (and contract_status = sent).

Response: 204 No Content

Errors:

StatusWhen
400Request not in approved_commercial or pagare_validated status
400Signed pagaré not yet validated (pagaré countries)
409Required documents not all approved
409DocuSign envelope already exists
404No active DocuSign contract config for this locale + person type

POST /credit-line-requests/webhooks/docusign

Receives DocuSign Connect event notifications. No JWT auth. Secured by HMAC-SHA256 via X-DocuSign-Signature-1 header.

Supports both flat envelope JSON (eSign legacy) and Connect 2.0 JSON format (restv2.1).

On a Completed envelope event:

  1. Looks up the request by docusign_envelope_id
  2. Updates contract_status to signed
  3. Downloads combined PDF from DocuSign and stores in S3
  4. Inserts a system comment on the linked ticket (i18n-resolved in the company's language)

Response: 200 OK (empty body)

Error responses:

StatusWhen
401HMAC signature missing or invalid
500DOCUSIGN_CONNECT_HMAC_SECRET not configured

POST /credit-line-requests/{id}/contract/signed-storage-sync

Manually triggers re-download of the signed contract PDF from DocuSign and re-upload to S3. Use this when the automatic webhook PDF persistence failed.

Response: 200 OK

json
{ "url": "https://s3.amazonaws.com/envia/contracts/..." }

DocuSign Credit Contracts (/docusign-credit-contracts)

Configuration registry that maps DocuSign Template IDs to locale + person type combinations. Used by the contract creation flow to resolve which DocuSign template to use.

Endpoint Summary

MethodPathDescriptionAuthPermission
GET/docusign-credit-contractsPaginated listtoken_admincontract-templates
POST/docusign-credit-contractsCreate new config entrytoken_admincreate-contract-templates
GET/docusign-credit-contracts/{id}Get single entrytoken_admincontract-templates
PATCH/docusign-credit-contracts/{id}Update config entrytoken_admincreate-contract-templates

GET /docusign-credit-contracts

Query Parameters:

ParamTypeRequiredDescription
startnumberNoOffset (default: 0)
lengthnumberNoPage size (default: 25)
sortBystringNoColumn to sort
sortTypestringNoasc or desc
localeIdnumberNoFilter by locale
personTypestringNokyb or kyc
isActivenumberNo0 or 1

Response (200):

json
{
  "draw": 25,
  "recordsTotal": 4,
  "recordsFiltered": 4,
  "data": [
    {
      "id": 1,
      "name": "Credit Line Contract MX KYB",
      "description": null,
      "docusign_template_id": "a1b2c3d4-...",
      "locale_id": 1,
      "country_code": "MX",
      "country_name": "Mexico",
      "person_type": "kyb",
      "is_active": 1,
      "created_by_name": "Admin User",
      "created_at": "2026-03-01T00:00:00Z"
    }
  ]
}

POST /docusign-credit-contracts

Creates a new DocuSign template config entry.

Payload:

json
{
  "name": "Credit Line Contract MX KYC",
  "description": "Template for individual clients in Mexico",
  "docusign_template_id": "d4e5f6...",
  "locale_id": 1,
  "person_type": "kyc",
  "is_active": true
}

Response (201):

json
{ "id": 5 }

Errors:

StatusWhen
409Another active entry already exists for this locale + person_type

PATCH /docusign-credit-contracts/{id}

Updates an existing config entry. Same payload as POST.

Response: 204 No Content


Client API Endpoints (queries repo)

These endpoints are served by the queries API and consumed by the client portal. They are documented here for cross-repo traceability.

MethodPathDescriptionAuth
GET/credit-line/eligibilityCheck if company is eligible to submit a credit line requestClient JWT
GET/credit-line/documentsGet required document checklist for the company's locale + person typeClient JWT
GET/credit-line/request/:ticket_idGet credit line request status and documents for a given ticketClient JWT
PATCH/credit-line/documents/:document_idResubmit a rejected documentClient JWT

GET /credit-line/eligibility

Response (200) — eligible:

json
{
  "eligible": true,
  "reason": null,
  "last_request_id": null,
  "ticket_id": null,
  "criteria": null
}

Response (200) — not eligible:

json
{
  "eligible": false,
  "reason": "criteria_not_met",
  "last_request_id": 7,
  "ticket_id": 1001,
  "criteria": {
    "antiquity": { "met": true, "value_days": 180, "required_days": 90 },
    "delivered_guides": { "met": false, "value": 200, "required": 300 },
    "used_balance": { "met": true, "value": 150000, "currency": "MXN" }
  }
}

Blocking reasons (not criteria): pending_commercial, approved_commercial, legal_review, active — returned directly as reason when a live request or active credit exists.


Common Error Responses

All errors follow the Boom format:

json
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Descriptive error message"
}
StatusWhen
400Validation failed (Joi schema)
401Missing or invalid JWT / invalid DocuSign HMAC
403Insufficient permissions for the action
404Resource not found
409Business rule conflict
500Internal server error or missing required env var

Envia Admin