ANSON API DOCUMENTATION

API接入文档

资产方通过统一 API 提交客户、借据、逾期和分期信息。安小信完成身份校验、数据校验、客户归户、资产建档和案件创建,并返回可查询的处理结果。

HMAC-SHA256每个资产方独立密钥
单条原子一条坏数据不阻断其他资产
租户隔离归属由验签身份确定
PII 加密姓名和完整号码密文保存
QUICK START

资产方研发从这里开始

联调前由安小信提供环境域名、Partner ID、Partner Secret 和已授权产品类型。资产方生成批次报文、计算摘要和签名,提交后按逐行结果完成对账。

STEP 01获取接入参数

取得 Base URL、Partner ID、Secret 及产品授权范围。Secret 只通过双方约定的安全渠道交付。

STEP 02组装并签名

先固定 JSON 原始请求体,再计算 rowsChecksum、bodyHash 和 HMAC 签名,发送时不得重新序列化。

STEP 03提交并逐行对账

保存 packageRef;读取 rowResults。只重推失败业务数据,并为新请求生成新的 nonce 和 Idempotency-Key。

接口的边界

本 API 同步完成客户归户、资产、账单、逾期状态和案件头建档,不创建外呼任务,也不会拨打电话。建档后的策略、合规审核和作业由安小信内部流程决定。

接入链路

一笔资产从哪里进、从哪里出

批次先通过请求级门槛,再按行独立处理。最终从 HTTP 响应和查询接口输出批次结果、案件编号与失败原因。

  1. 01身份验签确认 Partner、时间戳、nonce 和报文签名
  2. 02批次校验核对 JSON、数量、rows 摘要及 5000 条上限
  3. 03客户归户优先用 borrowerRef 关联同一客户的多笔借据
  4. 04逐行建档每行一个事务,写客户、资产、账单、逾期和案件
  5. 05结果回执返回 packageRef、caseRef、assetRef 和逐行错误
API V1

接口清单

下表路径相对于正式接入时分配的 Base URL。签名必须使用网关转发后应用实际收到的路径,联调时以安小信提供的环境参数为准。

方法路径鉴权幂等键用途
GET/partner/v1/health无需无需检查接入服务是否可用,不代表某个 Partner 的授权状态。
POST/partner/v1/documents/upload-credentialsHMAC无需按批次申请合同上传临时凭证;需接入环境启用 OSS。
POST/partner/v1/asset-packagesHMAC必填提交 1~5000 条资产,同步返回批次及逐行建档结果。
GET/partner/v1/asset-packages/{packageRef}HMAC无需查询批次汇总;异常明细位于 failedRows。
GET/partner/v1/asset-packages/{packageRef}/rows/{rowRef}HMAC无需查询一条资产的建档状态、caseRef、assetRef 或错误原因。

请求和响应编码均为 UTF-8,Content-Type 为 application/json。POST 最大行数为 5000。

合同上传接口

原协议还定义了 POST /partner/v1/documents/upload-credentials(申请合同上传凭证)。接口须由接入环境启用并绑定 OSS;未启用时返回 503。字段及上传流程见“合同上传”章节。

AUTHENTICATION

请求头与 HMAC 签名

除健康检查外,所有请求均须验签。时间戳允许与服务端相差 300 秒;nonce 只能成功使用一次,用于防止重放。

请求头是否必填格式说明
Content-Type必填application/jsonPOST 请求使用。
X-Partner-Id必填字符串安小信分配的资产方身份。
X-Timestamp必填Unix 秒例如 1789383600,与服务端时间差不得超过 300 秒。
X-Nonce必填唯一随机字符串每次 HTTP 请求重新生成;即使重试也不得复用。
X-Signature必填Base64HMAC-SHA256 的 Base64 结果。
Idempotency-Key资产包 POST 必填业务唯一字符串提交资产包时用于安全重试同一原始请求体;GET 和申请上传凭证不需要。

签名原文

5 行内容按顺序用换行符 \n 拼接,末尾不增加换行。

SIGN BASE
HTTP_METHOD
REQUEST_PATH
X_TIMESTAMP
X_NONCE
SHA256_RAW_BODY_HEX

计算规则

  • POST bodyHash对实际发送的 UTF-8 原始 JSON 字节计算 SHA-256 小写 hex。
  • GET bodyHash对空字节串计算 SHA-256,小写 hex 为 e3b0c442...b855。
  • signatureBase64(HMAC-SHA256(partnerSecret, signBase))。
  • 路径只使用路径部分,不含域名与 query string;具体值以接入环境说明为准。
Node.js 签名示例
import { createHash, createHmac, randomUUID } from "node:crypto";

const method = "POST";
const path = "/partner/v1/asset-packages";
const baseUrl = process.env.ANSON_PARTNER_BASE_URL;
const partnerId = process.env.ANSON_PARTNER_ID;
const partnerSecret = process.env.ANSON_PARTNER_SECRET;
const timestamp = String(Math.floor(Date.now() / 1000));
const nonce = randomUUID();
const idempotencyKey = randomUUID();
const rawBody = JSON.stringify(payload); // 发送时必须原样使用 rawBody
const bodyHash = createHash("sha256").update(rawBody, "utf8").digest("hex");
const signBase = [method, path, timestamp, nonce, bodyHash].join("\n");
const signature = createHmac("sha256", partnerSecret)
  .update(signBase, "utf8")
  .digest("base64");

const response = await fetch(`${baseUrl}${path}`, {
  method,
  headers: {
    "Content-Type": "application/json",
    "X-Partner-Id": partnerId,
    "X-Timestamp": timestamp,
    "X-Nonce": nonce,
    "X-Signature": signature,
    "Idempotency-Key": idempotencyKey
  },
  body: rawBody
});
console.log(response.status, await response.json());
常见验签失败原因

计算签名后又格式化 JSON、签名路径与服务端实际路径不同、时间戳使用毫秒、重试时复用 nonce,都会返回 401。

REQUEST SCHEMA

POST 请求字段字典

金额统一按“元”传递,推荐使用字符串并保留最多两位小数;日期使用 YYYY-MM-DD,时间使用 ISO 8601。

枚举字段只能填写列出的英文取值,括号内为中文含义,不作为接口传参。还款方式为字符串字段,下方列出建议取值。

批次报文

request body
字段必填类型约束与说明
schemaVersion必填string固定为 partner-asset-package.v1。
batchRef必填string资产方侧批次号;同一 Partner 下应稳定唯一。
submittedAt必填datetime资产方生成批次的 ISO 8601 时间。
rowCount必填integer1~5000,必须等于 rows.length。
rowsChecksum必填string对 JSON.stringify(rows) 的 UTF-8 字节计算 SHA-256 小写 hex。
rows必填array资产行数组;每一行独立事务建档。

资产行与借据信息

rows[] / loanInfo
字段必填类型约束与说明
rows[].rowRef必填string批次内唯一的单行编号,用于逐行查询和对账。
loanInfo.contractRef必填string资产方借据或合同编号;同一 Partner 和工作空间内不可重复建档。
loanInfo.borrowerRef选填string资产方稳定客户号,强烈建议提供。同一客户多笔借据使用同一个值。
loanInfo.borrowerName必填string借款人姓名,通过 TLS 传输,进入安小信边界后加密保存。
loanInfo.borrowerPhone必填string中国大陆 11 位手机号或 E.164 格式。
loanInfo.productCategory选填enum(枚举)consumer_credit(消费信贷)、business_loan(经营性贷款)、credit_card_installment(信用卡分期)、other(其他);还须在 Partner 授权范围内。
loanInfo.productName选填string资产方产品名称。
loanInfo.originatorMerchantId选填string原始放款机构或商户标识。
loanInfo.principalAmount必填decimal string原始本金,单位元,非负且最多两位小数。
loanInfo.interestRate选填decimal string年化利率百分数,如 18.00 表示 18%。
loanInfo.repaymentMethod选填string建议使用 equal_installment(等额本息)、interest_first(先息后本)、lump_sum(到期一次性还本付息) 或 other(其他)。
loanInfo.loanTerm选填integer借款期数,至少为 1。
loanInfo.disbursementDate选填date放款日期,YYYY-MM-DD。
loanInfo.finalDueDate选填date最终到期日,YYYY-MM-DD。

逾期信息

overdueInfo
字段必填类型约束与说明
overdueDays必填integer当前逾期天数 DPD,非负整数。
firstOverdueInstallmentNo选填integer最早逾期期次,至少为 1。
remainingPrincipal必填decimal string剩余未还本金,单位元。
overdueAmount必填decimal string当前逾期总金额,单位元。
overduePrincipal选填decimal string逾期本金,单位元。
overdueService选填decimal string逾期服务费,单位元。
penaltyAmount选填decimal string罚息或违约费用,单位元。

联系人

contactInfo[] · optional
字段必填类型约束与说明
role数组项必填enum(枚举)emergency_contact(紧急联系人) 或 guarantor(担保人)。借款人本人联系方式直接取自 loanInfo。
relationship选填string与借款人的关系。
name选填string联系人姓名。
phone数组项必填string中国大陆 11 位手机号或 E.164 格式。

还款计划

repaymentPlan[] · optional
字段必填类型约束与说明
installmentNo数组项必填integer期次编号,至少为 1。
dueDate选填date应还日期,YYYY-MM-DD。
dueAmount选填decimal string本期应还金额,单位元。
paidAmount选填decimal string本期已还金额,单位元。
overduePrincipal选填decimal string本期逾期本金。
overdueService选填decimal string本期逾期服务费。
penaltyAmount选填decimal string本期罚息或违约费用。
status选填enum(枚举)pending(待还款)、overdue(已逾期) 或 settled(已结清)。

合同文档元数据

contractDocument · optional
字段必填类型约束与说明
fileName对象内必填string必须等于本行 contractRef 加小写扩展名,支持 pdf、ofd、png、jpg、jpeg、webp。
docHash对象内必填string文件 SHA-256,小写 64 位 hex。
objectRef选填string可省略;如提供,必须等于平台分配的 uploadPrefix 加本行 fileName,不接受外部地址或其他目录。
合同引用与校验

先申请上传凭证并将合同写入指定 OSS 目录,再提交文件名和 SHA-256。平台读取实际文件、验证绑定和哈希,归档成功后才记录为已验证。文件上限 20 MiB,不支持空文件。

租户归属不能由报文指定

partnerId、tenantKey、workspaceId、ownerRef 等归属字段禁止出现在行数据中。系统只根据验签成功的 Partner 注册关系确定租户和工作空间。

合同上传

申请合同上传凭证与文件校验

原协议将合同文件上传与资产报文提交分开:按批次领取临时凭证,将合同直接上传至平台 OSS,再提交文件引用。

启用条件:接入环境已配置 OSS

接口已实现按批次签发凭证、文件名绑定、文件读取和 SHA-256 校验。校验通过后,将文件归档到资产方无写权限的目录,再将文档标记为已验证。未配置存储时返回服务不可用,不会跳过校验。

POST /partner/v1/documents/upload-credentials

使用 Partner 的 HMAC 签名请求头。每批申请一次凭证,同一批次内所有合同使用该凭证上传。请求仅接受 batchRef 和 docFormat。partnerId、batchRef、contractRef 必须以字母或数字开头,后续只允许字母、数字及 . _ : -,最长 191 字符;完整文件路径最长 500 字符。

请求字段必填说明
batchRef是资产方批次号,与后续资产包中的批次号相同。
docFormat是本批合同统一格式:pdf(PDF 文件)、image(图片)、ofd(OFD 电子文档)。
申请合同上传凭证示例
{"batchRef":"batch-20260914-001","docFormat":"pdf"}

响应字段(HTTP 200)

字段含义
uploadPrefix允许写入的目录:partner-docs/{partnerId}/{batchRef}/。
namingRule文件名规则:{contractRef}.{ext},合同号取自对应资产的 loanInfo.contractRef。
credentials.accessKeyId临时访问密钥 ID。
credentials.accessKeySecret临时访问密钥 Secret。
credentials.securityToken临时安全令牌。
credentials.expiration凭证到期时间。原协议建议 45 分钟内完成上传,实际使用以返回的到期时间为准。
bucket / endpoint / regionOSS 存储桶、服务地址与地域。

合同与资产绑定流程

  1. 以 batchRef 和 docFormat 申请本批上传凭证,写权限仅覆盖返回的目录。
  2. 通过 OSS SDK 将文件上传到 {uploadPrefix}{contractRef}.{ext}。
  3. 提交资产包时填写 contractDocument.fileName 和文件内容的 SHA-256 摘要 docHash。
  4. 平台根据验签身份、批次号和合同号确定文件路径,验证文件名、对象存在性及内容哈希。

合同校验错误

资产包仍按行处理:正常受理的批次返回 HTTP 201,合同错误在对应 rowResults 中返回,其他行继续建档。下表 422 为行校验错误分类。存储不可用返回行错误 document_storage_unavailable(可重试);空文件和超限分别为 contract_document_empty、contract_document_too_large。

校验状态错误码中文含义
422contract_document_filename_mismatch文件名与本行合同号及扩展名不匹配。
422contract_document_not_found对应目录下找不到合同文件。
422contract_document_hash_mismatch文件内容的哈希与报文声明不一致。

合同引用为可选组;选择提供合同后,应按上述流程绑定文件。查看原协议。

FULL REQUEST

完整请求报文

示例同时展示客户、借据、逾期、联系人、还款计划和合同元数据。示例 rowsChecksum 与下方 rows 的紧凑 JSON 序列化结果对应;修改任一字段后必须重新计算。

POST /partner/v1/asset-packages
{
  "schemaVersion": "partner-asset-package.v1",
  "batchRef": "batch-20260914-001",
  "submittedAt": "2026-09-14T19:00:00+08:00",
  "rowCount": 1,
  "rowsChecksum": "f9e8c366edb64d8a33fa42050f502ee0e0c0939cf8f0d3689c2e78a9752b3014",
  "rows": [
    {
      "rowRef": "row-001",
      "loanInfo": {
        "contractRef": "loan-001",
        "borrowerRef": "customer-001",
        "borrowerName": "张三",
        "borrowerPhone": "",
        "productCategory": "consumer_credit",
        "productName": "消费分期",
        "principalAmount": "12000.00",
        "interestRate": "18.00",
        "repaymentMethod": "equal_installment",
        "loanTerm": 12,
        "disbursementDate": "2026-06-01",
        "finalDueDate": "2027-05-01"
      },
      "overdueInfo": {
        "overdueDays": 45,
        "firstOverdueInstallmentNo": 3,
        "remainingPrincipal": "9360.00",
        "overdueAmount": "1260.50",
        "overduePrincipal": "1100.00",
        "overdueService": "120.50",
        "penaltyAmount": "40.00"
      },
      "contactInfo": [
        {
          "role": "emergency_contact",
          "relationship": "配偶",
          "name": "李四",
          "phone": ""
        }
      ],
      "repaymentPlan": [
        {
          "installmentNo": 3,
          "dueDate": "2026-07-31",
          "dueAmount": "1260.50",
          "paidAmount": "0.00",
          "overduePrincipal": "1100.00",
          "overdueService": "120.50",
          "penaltyAmount": "40.00",
          "status": "overdue"
        }
      ],
      "contractDocument": {
        "fileName": "loan-001.pdf",
        "docHash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
        "objectRef": "partner-docs/partner-demo-001/batch-20260914-001/loan-001.pdf"
      }
    }
  ]
}

rowsChecksum 计算

Node.js
const rowsJson = JSON.stringify(payload.rows);
payload.rowsChecksum = createHash("sha256")
  .update(rowsJson, "utf8")
  .digest("hex");

// 最后再固定整个请求体;签名和 HTTP 请求必须使用同一个 rawBody
const rawBody = JSON.stringify(payload);
RESPONSE

响应、状态和对账方式

POST 正常受理返回 HTTP 201。即使其中一行数据错误,只要批次门槛通过,仍返回 201,并在 rowResults 标明每行结果。

层级状态含义
批次admitted(全部成功)全部资产行建档成功。
批次admitted_with_restrictions(部分成功)部分成功、部分失败;成功行不回滚。
批次rejected(已拒绝)批次通过请求级门槛,但没有任何一行建档成功。
行build_success(建档成功)该行已完成客户、资产、账单、逾期状态和案件头建档。
行rejected(已拒绝)确定性业务或数据错误;修正该行后再作为新请求提交。
行build_failed(建档失败)建档阶段异常,可在确认原因后重试。
与原协议的状态含义有差异

原协议采用自动校验后人工准入流程,admitted 表示已准入,admitted_with_restrictions 表示有条件准入。当前实现采用同步逐行建档,上表两值分别表示全部建档成功和部分成功,不能据此认定已完成人工准入或合同校验。

部分成功响应示例

HTTP 201
{
  "packageRef": "partner-package:example",
  "batchRef": "batch-20260914-001",
  "status": "admitted_with_restrictions",
  "qualityGrade": "needs_review",
  "validatedRowCount": 1,
  "admittedRowCount": 1,
  "rejectedRowCount": 1,
  "idempotentReplay": false,
  "rowResults": [
    {
      "rowPosition": 1,
      "rowRef": "row-001",
      "status": "build_success",
      "errorCode": null,
      "errorField": null,
      "errorMessage": null,
      "caseRef": "case:partner:example",
      "assetRef": "asset:partner:example"
    },
    {
      "rowPosition": 2,
      "rowRef": "row-002",
      "status": "rejected",
      "errorCode": "invalid_request",
      "errorField": "rows[].loanInfo.borrowerPhone",
      "errorMessage": "手机号格式不正确",
      "caseRef": null,
      "assetRef": null
    }
  ]
}
大批次查询的返回方式

GET 批次接口返回数量汇总,并只把异常行放在 failedRows 中,避免 5000 条成功明细拖大响应。需要核对某一成功行时,使用逐行查询接口。

DELIVERY SEMANTICS

单条原子、幂等与重复数据

先判断“整批能否读取和信任”,再判断“每一行能否建档”。因此 5000 条里一条手机号错误,只失败这一条,其余 4999 条照常建档。

整批拒绝的情况

  • 签名、时间戳或 nonce 校验失败
  • 请求体不是合法 JSON
  • schemaVersion 不支持
  • rowCount 与 rows.length 不一致
  • rowsChecksum 不一致
  • rows 为空或超过 5000 条

只失败单行的情况

  • 必填字段缺失或格式错误
  • 产品类型未获授权
  • 批次内 rowRef 重复
  • 同一 Partner 下借据已存在
  • 报文试图指定租户归属字段
  • 该行数据库事务执行失败

重试和重复语义

场景结果资产方处理建议
同一 Idempotency-Key + 完全相同原始请求体返回第一次结果,idempotentReplay=true网络超时后可安全重试,不会重复建档。
同一 Idempotency-Key + 不同请求体HTTP 409 idempotency_conflict修正调用逻辑,使用新的幂等键。
同一 batchRef + 相同业务内容返回原批次结果按原 packageRef 对账。
同一 batchRef + 内容变化HTTP 409 batch_content_conflict生成新的 batchRef 后提交。
同一 contractRef 再次建档该行 asset_already_exists不要重推成功借据;只提交已修正的失败借据。
同一 borrowerRef 的另一笔 contractRef复用客户档案,创建新资产和案件保证 borrowerRef 长期稳定。
borrowerRef 缺失时的退化行为

为了兼容旧调用可以不传 borrowerRef,系统会退化为按 contractRef 建立客户,无法把同一人的多笔借据归到同一客户。

ERROR CODES

HTTP 与业务错误码

请求级错误通过非 2xx HTTP 状态返回;行级错误出现在 201 响应的 rowResults 或查询结果中。

请求级错误

统一错误响应结构
{
  "code": "rows_checksum_mismatch",
  "message": "rowsChecksum 校验失败"
}
HTTPerrorCode触发条件建议
400invalid_json请求体不是合法 JSON。检查编码和 JSON 语法。
400invalid_request批次结构错误、缺字段、为空或超过 5000 条。按 errorField 修正请求。
400unsupported_schema_versionschemaVersion 不受支持。使用 partner-asset-package.v1。
422row_count_mismatchrowCount 与实际 rows 数量不一致。重新计算数量。
422rows_checksum_mismatchrowsChecksum 与实际 rows 摘要不一致。对最终 rows 紧凑序列化后重算。
401unauthenticated签名错误、时间戳超窗或 nonce 已使用。重新生成 timestamp、nonce 和签名。
403partner_not_registeredPartner 不存在、已停用或未绑定环境。联系安小信核对接入注册。
409idempotency_conflict幂等键相同但请求体不同。使用新幂等键。
409batch_content_conflictbatchRef 相同但批次内容不同。使用新 batchRef。
404package_not_found / row_not_found在当前 Partner 范围内找不到批次或行。核对 packageRef、rowRef 和环境。
500internal_error服务端未预期异常。保留请求标识并联系支持。
503status=unavailable健康检查发现服务不可用。稍后重试。

行级错误

errorCode含义是否建议原样重试
invalid_request该行字段缺失、类型或格式不正确。否,先修正字段。
ownership_field_forbidden该行携带了由服务端管理的租户归属字段。否,删除字段。
product_not_allowed产品类型不在 Partner 授权范围。否,先核对授权。
duplicate_row_ref同一批次内 rowRef 重复。否,修正 rowRef。
asset_already_exists借据已经成功建档。否,查询原资产。
row_build_failed单行建档事务异常。确认服务状态后可重试。
SECURITY

敏感数据与租户隔离

生产环境通过 HTTPS 传输。资产方身份完成验签后,服务端根据注册关系确定工作空间,报文不能覆盖租户归属。

IN TRANSITTLS 传输

姓名、手机号等业务原文只放在 HTTPS 请求体中,Secret 不放 URL 和请求体。

AT RESTPII 加密

姓名和完整号码进入安小信边界后使用 AES-256-GCM 密文保存。

ISOLATION服务端定归属

Partner 注册关系绑定租户和工作空间,查询同样受 Partner 身份范围限制。

生产密钥管理

每个资产方使用独立 Secret。请存放在密钥管理服务或受控环境变量中,不写入代码、日志、截图或工单;泄露后应立即申请轮换。

申请接入

先用一小批脱敏样本完成联调

邮件说明机构、资产类型、预计批量和回执需求,我们会提供接入参数与联调支持。

联系接入支持