Janvia

接口

回调通知

任务结束后,我们把结果 POST 到你的地址,附上签名,没收到会重试。

提交任务时传 callback_url(必须是 https 公网地址)。任务完成或失败后,我们向这个地址 POST 任务对象,内容和 GET /v1/tasks/TASK_ID 返回的一样。

请求带这几个 Header:

Content-Type: application/json
X-Relay-Task-Id: task_9f2c...
X-Relay-Attempt: 1
X-Relay-Signature: 9b1d0c...

重试

你的服务返回 2xx 就算收到。返回其他状态码、10 秒内没有响应、或者返回跳转(我们不跟随跳转),都算失败,稍后会再发:总共最多 6 次,分别在 10 秒、1 分钟、5 分钟、30 分钟、2 小时后重发。X-Relay-Attempt 是第几次发送。

同一个任务可能收到不止一次,比如你的响应在回程中丢了。请按任务 ID 去重,每个任务只处理一次。

送达状态

任务的 callback 字段记录了送达情况:

"callback": {
  "url": "https://example.com/hooks/relay",
  "status": "delivered",
  "attempts": 1,
  "last_attempt_at": "2026-10-21T08:00:13.000Z",
  "last_error": null
}
  • status:waiting(任务还在运行)、pending(正在发送或等待重试)、delivered(已送达)、failed(全部尝试都失败了)。
  • last_error:最近一次失败的原因,比如 HTTP 500 或 no response within 10s。

控制台的任务详情里也能看到。

重新发送

想再收一次已结束任务的回调(比如修好了接收地址),在控制台的任务详情里点 重新发送,或者调用:

curl -X POST https://janvia.io/v1/tasks/TASK_ID/callback \
  -H "Authorization: Bearer $API_KEY"

会重新开始一轮发送,返回 202 和任务对象。任务还在运行、或者没有 callback_url 时返回 400。

回调只是通知。没收到时,用 GET /v1/tasks/TASK_ID 查询结果即可。在线试用提交的任务没有回调。

验证签名

X-Relay-Signature 是对原始请求体做 HMAC-SHA256 的十六进制结果。密钥是提交这个任务的 API 密钥的 SHA-256 十六进制字符串。请用收到的原始字节计算,不要把 JSON 解析后再重新序列化。如果这个密钥已经删除,请求不带签名。

Node.js:

import { createHash, createHmac, timingSafeEqual } from 'node:crypto';

function verify(rawBody, signature, apiKey) {
  const secret = createHash('sha256').update(apiKey).digest('hex');
  const expected = createHmac('sha256', secret).update(rawBody).digest('hex');
  return (
    signature.length === expected.length &&
    timingSafeEqual(Buffer.from(signature), Buffer.from(expected))
  );
}

Python:

import hashlib, hmac

def verify(raw_body: bytes, signature: str, api_key: str) -> bool:
    secret = hashlib.sha256(api_key.encode()).hexdigest().encode()
    expected = hmac.new(secret, raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, signature)