Pix聚合AI DOCS
API 密钥 控制台
概览 鉴权 媒体生成 Dola 全能30秒 响应 错误码
DEVELOPER API

开发文档

使用 API 密钥接入 Pix聚合AI 的图片、视频与音频生成能力。所有示例均对应当前已实现接口,未开放能力会明确标记。

服务正常 HTTPS JSON Bearer Token
API 基础地址https://fei85.cn
QUICKSTART

三步完成首次接入

  1. 01
    创建 API 密钥

    登录控制台,在“API 密钥”中创建密钥并按需设置权限、有效期和额度上限。

  2. 02
    确认模型 ID

    在创作控制台选择可用模型,接口参数使用模型的实际 ID,不使用展示名称。

  3. 03
    发送 HTTPS 请求

    将密钥放入 Authorization 请求头,正文使用 UTF-8 JSON。

AVAILABILITY

能力状态

这里区分已经通过 API 密钥开放的能力和仅在站内可用的能力。

能力状态说明
图片 / 视频 / 音频生成Beta

POST /api/generate,已接入 Bearer Token。

Chat 模型 API即将开放

站内已有 Chat 模型,但外部聊天接口尚未发布。

API 密钥状态轮询Beta

GET /api/check_record?id=记录ID,使用同一 Bearer Token 鉴权,可在无人值守服务中轮询。

不要接入不存在的兼容路径

当前没有对外发布 /v1/chat/completions。在正式开放前,请勿将站内 Chat 请求当作服务端 API 使用。

AUTHENTICATION

身份验证

API 密钥只应保存在服务端。每个请求都需要在 HTTP 请求头中携带 Bearer Token。

HTTP HEADER
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
密钥权限

建议按应用分别创建密钥,只勾选需要的图片、视频或音频权限,并设置额度上限和有效期。

POST/api/generate
Beta

提交图片、视频或音频生成任务。接口成功后返回记录 ID、初始状态与本次预计扣点。

cURL
curl -X POST 'https://fei85.cn/api/generate' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"mode":"draw","model":"MODEL_ID","prompt":"一座未来城市的清晨,电影感光影"}'
REQUEST

请求字段

字段类型必填说明
modestring

draweditvideoaudio

modelstring

控制台中启用模型的实际 ID。

promptstring

生成提示词,使用 UTF-8 文本。

不同模型可能要求额外参数。以控制台该模型当前显示的参数为准;未知字段可能被忽略或返回 422。

RESPONSE

响应结构

200 OK
{
    "ok": true,
    "record_id": 1024,
    "record_ids": [
        1024
    ],
    "status": "queued",
    "status_url": "/api/check_record?id=1024",
    "credits": {
        "normal": 98.5,
        "member": 0
    },
    "cost": 1.5,
    "created": true
}
record_id

本次任务的主记录 ID。

status

初始状态,通常为 queued

cost

本次任务记录的扣点数。

created

是否新建了任务记录。

关于 status_url

响应会返回 status_url(即 /api/check_record?id=记录ID)。使用提交时相同的 Bearer Token 轮询该地址即可获取最新状态与结果地址;当 record.statussucceeded 时即可取用结果。

POLLING

状态轮询

提交后使用响应里的 status_url 轮询任务进度。轮询接口同样需要 Bearer Token,可在服务端定时调用。

cURL
curl 'https://fei85.cn/api/check_record?id=记录ID' \
  -H 'Authorization: Bearer YOUR_API_KEY'
record.status

queued / running 继续轮询;succeeded 取用结果;failed 任务失败。

record.image_url / video_url / audio_url

成功后的结果地址。

records

批量任务的各子任务状态数组。

LIFECYCLE

完整调用时序

从提交到取回结果的一次完整往返。提交与轮询使用同一个 Bearer Token。

SEQUENCE
客户端                              灵智 AI 服务端
  │                                        │
  │  1. POST /api/generate                 │
  │     Authorization: Bearer YOUR_API_KEY │
  │     { mode, model, prompt }            │
  ├───────────────────────────────────────►│
  │                                        │  校验令牌 / 权限 / 预扣点
  │  2. 200 OK                             │
  │     { record_id, status=queued,        │
  │       status_url, cost }               │
  │◄───────────────────────────────────────┤
  │                                        │
  │  3. GET /api/check_record?id=记录ID    │
  │     Authorization: Bearer YOUR_API_KEY │
  ├───────────────────────────────────────►│
  │     { status: running }             │
  │◄───────────────────────────────────────┤   ← 未完成,间隔重试
  │                                        │
  │  3'. GET /api/check_record?id=记录ID   │
  ├───────────────────────────────────────►│
  │     { status: succeeded,               │
  │       image_url: "https://..." }       │
  │◄───────────────────────────────────────┤
  │                                        │
  │  4. 取用 record.image_url / video_url  │
  └────────────────────────────────────────┘
第 1 步

提交任务,拿到 record_idstatus_url

第 2 步

服务端返回初始状态(通常 queued)与本次预计扣点 cost

第 3 步

用同一 Token 轮询 status_urlrunning 时按 3~5 秒间隔重试。

第 4 步

status=succeeded 后从 image_url / video_url / audio_url 取结果。

轮询建议

不要高频轮询。建议在 queued / running 时每 3~5 秒查询一次,直到终态(succeeded / failed / cancelled)。单图通常数十秒,视频可能数分钟。

POST/api/generate
Dola 全能30秒

第三方网站接入 Dola 全能30秒时,必须由服务端同时发送字符串模型键和两个数字模型 ID。任务采用异步受理,提交成功不立即返回视频地址。

最重要的异步语义

ok=true + status=queued + record_id 表示任务已经成功受理。此时没有 video_url 是正常行为;保存 record_id 后再由 Worker 轮询,不能立即报“生成提供商未返回可用结果”。

字段类型必填固定值 / 规则
modestring

固定 video

modelstring

固定 dola-30s

model_idinteger

当前固定 1;新网站上线前重新核对模型页。

ai_model_idinteger

当前固定 1,与 model_id 同时发送。

promptstring

非空 UTF-8 文本,描述内容、动作、镜头与风格。

durationinteger

固定整数 30,不要允许前端覆盖。

resolutionstring

固定字符串 720P

ratiostring

使用下方支持画幅;未选择或 auto 时服务端改为 16:9

aspect_ratiostring

ratio 保持一致的兼容字段。

sizestring

使用画幅对应的 720P 尺寸。

reference_image_urlsarray

0 至 9 张去重后的公网 HTTPS 参考图;纯文本任务建议省略。

SERVER ENVIRONMENT
FEI85_API_BASE=https://fei85.cn
FEI85_API_KEY=YOUR_API_KEY
FEI85_DOLA_MODEL=dola-30s
FEI85_DOLA_MODEL_ID=1
FEI85_DOLA_AI_MODEL_ID=1

不要把任意前端 JSON 原样透传给 fei85。服务端必须使用字段白名单,并强制覆盖模型、时长和清晰度。

DOLA REFERENCES

参考图与画幅

Dola 支持纯文本生成,也支持 1 至 9 张参考图。参考图必须先由你的网站转换为 fei85 可持续访问的公网地址。

画幅ratio / aspect_ratiosize
横屏16:9

1280x720

竖屏9:16

720x1280

方形1:1

720x720

竖向传统3:4

720x960

横向传统4:3

960x720

超宽屏21:9

1680x720

  • 数量与去重最多 9 张,服务端去重;0 张时省略 reference_image_urls
  • 只允许公网 HTTPS拒绝 HTTP、localhost、内网 IP、file://、浏览器 blob: 和带用户名密码的 URL。
  • 保持持续可访问URL 在生成期间不能过期;短时签名地址必须覆盖排队和生成时长。
  • 服务端校验下载前校验协议、主机及 DNS 解析结果,限制重定向并保持 TLS 证书校验开启。
DOLA EXAMPLES

可直接使用的提交与轮询示例

以下示例均只使用占位密钥。JavaScript 示例面向 Node.js 18+ 服务端,不能放入浏览器。

cURL
curl -X POST 'https://fei85.cn/api/generate' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json; charset=utf-8' \
  --data-raw '{
    "mode": "video",
    "model": "dola-30s",
    "model_id": 1,
    "ai_model_id": 1,
    "prompt": "保持人物身份与服装一致,人物转身看向镜头,镜头缓慢拉近",
    "duration": 30,
    "resolution": "720P",
    "ratio": "9:16",
    "aspect_ratio": "9:16",
    "size": "720x1280",
    "reference_image_urls": [
      "https://media.example.com/reference-1.jpg",
      "https://media.example.com/reference-2.jpg"
    ]
  }'

# 保存提交响应中的 record_id;3 至 5 秒后使用同一 Token 查询。
curl 'https://fei85.cn/api/check_record?id=1024' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Accept: application/json'
PHP 8+
<?php
declare(strict_types=1);

const FEI85_BASE = 'https://fei85.cn';
const DOLA_SIZES = [
    '16:9' => '1280x720', '9:16' => '720x1280', '1:1' => '720x720',
    '3:4' => '720x960', '4:3' => '960x720', '21:9' => '1680x720',
];

$apiKey = trim((string)getenv('FEI85_API_KEY'));
if ($apiKey === '' || preg_match('/[\x00-\x20\x7f]/', $apiKey)) {
    throw new RuntimeException('FEI85_API_KEY is required.');
}

function fei85Request(string $apiKey, string $method, string $path, ?array $payload = null): array
{
    $curl = curl_init(FEI85_BASE . $path);
    $headers = ['Accept: application/json', 'Authorization: Bearer ' . $apiKey];
    $options = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => $method === 'POST' ? 120 : 60,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_FOLLOWLOCATION => false,
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_HTTPHEADER => $headers,
    ];
    if ($payload !== null) {
        $options[CURLOPT_POSTFIELDS] = json_encode(
            $payload,
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR
        );
        $options[CURLOPT_HTTPHEADER][] = 'Content-Type: application/json; charset=utf-8';
    }
    curl_setopt_array($curl, $options);
    $body = curl_exec($curl);
    $status = (int)curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
    $error = curl_error($curl);
    curl_close($curl);
    if (!is_string($body)) throw new RuntimeException('fei85 transport error: ' . $error);
    $data = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
    if ($status < 200 || $status >= 300 || ($data['ok'] ?? false) !== true) {
        throw new RuntimeException('fei85 HTTP ' . $status . ': ' . (string)($data['message'] ?? 'Request failed'));
    }
    return $data;
}

function dolaResultUrl(array $record): string
{
    $media = is_array($record['media'] ?? null) ? $record['media'] : [];
    foreach ([$record['video_url'] ?? '', $record['video_src'] ?? '', $record['media_src'] ?? '', $media['url'] ?? ''] as $value) {
        $url = trim((string)$value);
        if (str_starts_with($url, '/uploads/')) $url = FEI85_BASE . $url;
        $parts = parse_url($url);
        if (filter_var($url, FILTER_VALIDATE_URL) !== false
            && is_array($parts)
            && in_array(strtolower((string)($parts['scheme'] ?? '')), ['http', 'https'], true)
            && !isset($parts['user'], $parts['pass'])) {
            return $url;
        }
    }
    return '';
}

$ratio = '16:9';
$references = array_values(array_unique(['https://media.example.com/character.jpg']));
if (count($references) > 9) throw new InvalidArgumentException('Dola accepts at most 9 references.');
foreach ($references as $value) {
    $parts = parse_url($value);
    if (filter_var($value, FILTER_VALIDATE_URL) === false
        || !is_array($parts)
        || strtolower((string)($parts['scheme'] ?? '')) !== 'https'
        || isset($parts['user'], $parts['pass'])) {
        throw new InvalidArgumentException('Reference images must use public HTTPS URLs.');
    }
}

$submitted = fei85Request($apiKey, 'POST', '/api/generate', [
    'mode' => 'video',
    'model' => 'dola-30s',
    'model_id' => 1,
    'ai_model_id' => 1,
    'prompt' => '保持主体身份一致,镜头平稳向前推进',
    'duration' => 30,
    'resolution' => '720P',
    'ratio' => $ratio,
    'aspect_ratio' => $ratio,
    'size' => DOLA_SIZES[$ratio],
    'reference_image_urls' => $references,
]);

$recordId = filter_var($submitted['record_id'] ?? null, FILTER_VALIDATE_INT);
if (!is_int($recordId) || $recordId < 1) throw new RuntimeException('Invalid record_id.');
// queued + record_id 表示成功受理;保存 record_id,再由 Worker 异步轮询。

$pending = ['queued', 'pending', 'running', 'processing'];
$success = ['succeeded', 'success', 'completed', 'finished', 'done'];
$failure = ['failed', 'failure', 'error', 'cancelled', 'canceled', 'refunded'];
$deadline = time() + 1200;
while (time() < $deadline) {
    sleep(4);
    $data = fei85Request($apiKey, 'GET', '/api/check_record?id=' . rawurlencode((string)$recordId));
    $record = is_array($data['record'] ?? null) ? $data['record'] : [];
    $status = strtolower(trim((string)($record['status'] ?? $record['status_key'] ?? '')));
    if (in_array($status, $pending, true)) continue;
    if (in_array($status, $success, true)) {
        $url = dolaResultUrl($record);
        if ($url === '') throw new RuntimeException('Task succeeded without a usable video URL.');
        echo $url . PHP_EOL;
        break;
    }
    if (in_array($status, $failure, true)) throw new RuntimeException('Dola generation failed.');
    throw new RuntimeException('Unknown Dola status: ' . $status);
}
Node.js 18+
// Node.js 18+ 服务端示例。不要把 API Key 放进浏览器代码。
const API_BASE = 'https://fei85.cn';
const API_KEY = process.env.FEI85_API_KEY;
if (!API_KEY) throw new Error('FEI85_API_KEY is required');

const references = ['https://media.example.com/character.jpg'];
if (references.length > 9) throw new Error('Dola accepts at most 9 references');
for (const value of references) {
  const url = new URL(value);
  if (url.protocol !== 'https:' || url.username || url.password) {
    throw new Error('Reference images must use public HTTPS URLs');
  }
}

async function request(path, options = {}) {
  const response = await fetch(API_BASE + path, {
    ...options,
    headers: {
      Accept: 'application/json',
      Authorization: `Bearer ${API_KEY}`,
      ...(options.body ? {'Content-Type': 'application/json; charset=utf-8'} : {}),
    },
  });
  const data = await response.json();
  if (!response.ok || data.ok !== true) {
    throw new Error(data.message || `fei85 HTTP ${response.status}`);
  }
  return data;
}

function resultUrl(record) {
  const values = [
    record.video_url,
    record.video_src,
    record.media_src,
    record.media?.url,
  ];
  for (let value of values) {
    if (typeof value !== 'string' || !value.trim()) continue;
    value = value.trim();
    if (value.startsWith('/uploads/')) value = API_BASE + value;
    if (!/^https?:\/\//i.test(value)) continue;
    const url = new URL(value);
    if (!['http:', 'https:'].includes(url.protocol) || url.username || url.password) continue;
    return url.href;
  }
  return '';
}

const submitted = await request('/api/generate', {
  method: 'POST',
  body: JSON.stringify({
    mode: 'video',
    model: 'dola-30s',
    model_id: 1,
    ai_model_id: 1,
    prompt: '保持主体身份一致,镜头平稳向前推进',
    duration: 30,
    resolution: '720P',
    ratio: '16:9',
    aspect_ratio: '16:9',
    size: '1280x720',
    reference_image_urls: references,
  }),
});

const recordId = Number(submitted.record_id);
if (!Number.isInteger(recordId) || recordId < 1) throw new Error('Invalid record_id');
// queued + record_id 就是成功受理;此时没有视频 URL 属于正常情况。

const pending = new Set(['queued', 'pending', 'running', 'processing']);
const succeeded = new Set(['succeeded', 'success', 'completed', 'finished', 'done']);
const failed = new Set(['failed', 'failure', 'error', 'cancelled', 'canceled', 'refunded']);
const deadline = Date.now() + 20 * 60 * 1000;

while (Date.now() < deadline) {
  await new Promise(resolve => setTimeout(resolve, 4000));
  const data = await request(`/api/check_record?id=${recordId}`);
  const status = String(data.record?.status ?? data.record?.status_key ?? '').toLowerCase();
  if (pending.has(status)) continue;
  if (succeeded.has(status)) {
    const url = resultUrl(data.record || {});
    if (!url) throw new Error('Task succeeded without a usable video URL');
    console.log({recordId, videoUrl: url});
    break;
  }
  if (failed.has(status)) throw new Error(data.record?.error_message || 'Dola generation failed');
  throw new Error(`Unknown Dola status: ${status}`);
}
GET/api/check_record?id={record_id}
Bearer Token

轮询时使用提交任务时相同的 Bearer Token。推荐每 3 至 5 秒查询一次,并在任何成功或失败终态立即停止。

本地状态兼容状态处理方式
pendingqueued / pending / running / processing

3 至 5 秒后继续轮询。

succeededsucceeded / success / completed / finished / done

停止轮询,按兼容顺序提取结果 URL。

failedfailed / failure / error / cancelled / canceled / refunded

停止轮询,记录脱敏后的错误。

record.video_url

第一优先级。

record.video_src

第二优先级。

record.media_src

第三优先级。

record.media.url

第四优先级。

相对地址兼容

仅将 /uploads/... 基于 https://fei85.cn 补全为绝对地址。最终结果只接受 httphttps URL,并拒绝本机、内网及带凭据的地址。

  • 本地等待上限视频可设置 15 至 20 分钟等待上限;本地超时不代表上游失败,应保留 record_id 继续后台查询。
  • 只重试查询网络结果不明确时不要自动重复提交 POST /api/generate,否则可能重复创建任务和扣费。
  • 幂等结算不要在 queued 时退款,也不要仅凭前端提示结算;使用最终状态和本地业务规则幂等处理。
DOLA OPERATIONS

常见错误、安全发布与验收

固定字段属于当前已验证契约,但模型数字 ID、价格和可用状态可能调整。每个新网站上线前都要重新核对。

错误常见原因处理建议
401Key 无效或过期

检查 Bearer 格式、Key 状态与有效期。

402余额或额度不足

检查账户余额、Key 额度和当前模型价格。

403缺少视频权限

只增加所需的视频生成与记录查询权限。

409生成队列未启用

联系平台支持,不要无限重试提交。

422模型或参数无效

核对三个模型字段、固定时长、清晰度和画幅。

500服务异常

保留请求时间与脱敏响应,受控退避后查询。

找不到模型遗漏数字模型 ID

同时发送 model=dola-30smodel_id=1ai_model_id=1

没有结果把 queued 当失败

提交阶段只要求有效 record_id,视频结果必须轮询。

  1. 01
    只读核对

    确认生产版本、目标文件哈希、运行进程和当前模型 ID,不直接覆盖生产文件。

  2. 02
    隔离演练

    在独立目录运行 PHP 语法、请求体、响应、轮询、URL 与安全契约测试。

  3. 03
    备份与原子替换

    使用固定哈希门禁、独立备份、同目录临时文件和原子 rename;安装后哈希不符立即回滚。

  4. 04
    按运行方式重启 Worker

    Supervisor、Laravel Queue、Symfony Messenger、Node.js、PM2、Swoole、RoadRunner 或容器常驻进程发布后必须 reload 或 restart。仅文档变更无需重启生成 Worker。

  5. 05
    发布后验收

    核对新 PID、启动时间、RUNNING 状态、心跳、生产文件哈希和不访问上游的 Fixture 契约;页面检查桌面与手机无横向溢出。

真实验收需要单独授权

真实生成会产生费用。未获业务负责人明确授权时,只做静态、Fixture、页面、无效占位 Token 和运行状态检查,不提交生成、不扣费、不支付、不退款。

  • 密钥隔离API Key 只放服务端环境变量;禁止进入源码、前端、Git、日志和发布包。
  • 日志脱敏不记录完整 Authorization、真实 Key 或敏感提示词;错误消息限制长度并清除 Token。
  • TLS 与 URL保持证书与主机校验开启,拒绝私网 URL、危险重定向和带凭据地址。
  • 版本漂移新接入前重新确认 model_idai_model_id、价格、画幅和参考图数量,并更新 Fixture 契约。
MODELS

模型与能力

模型 ID、可用状态和参数由后台配置实时决定。不要把展示名称写死为模型 ID。

媒体生成模型

可通过创作控制台查看启用模型及其参数,随后使用实际模型 ID 调用 /api/generate

打开模型控制台
Chat 模型

当前站内共有 62 个 Chat 模型;外部聊天 API 尚未开放,因此本页不提供伪造的兼容调用示例。

即将开放
ERRORS

错误码

HTTP含义处理建议
401密钥无效

检查 Authorization 格式、密钥状态和有效期。

402额度或余额不足

检查密钥额度、账户余额与模型费用。

403权限不足

为密钥增加对应生成权限,或更换密钥。

409生成队列未启用

当前服务暂不可提交生成任务,请联系平台支持。

422参数错误

检查 mode、模型 ID、提示词和模型特定参数。

500服务器错误

保留请求时间和响应内容,稍后重试或联系支持。

BILLING

计费与额度

任务费用按后台当前模型价格扣除账户点数。API 密钥额度用于限制该密钥累计用量,不替代账户余额。

校验密钥检查权限与额度创建任务记录费用
价格读取

不要在客户端缓存长期价格。模型价格和可用状态可能调整,正式执行前应以控制台当前信息为准。

SECURITY

安全建议

  • 只在服务端保存密钥不要把密钥写入浏览器 JavaScript、移动端包或公开仓库。
  • 使用环境变量为开发、测试、生产分别创建密钥,避免共用。
  • 采用最小权限仅开放应用需要的接口,并设置额度和有效期。
  • 定期轮换密钥疑似泄漏时立即禁用并创建新密钥。
  • 脱敏日志日志中只记录密钥前后缀,不记录完整 Authorization 请求头。