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是

draw、edit、video 或 audio。

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.status 为 succeeded 时即可取用结果。

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_id 与 status_url。

第 2 步

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

第 3 步

用同一 Token 轮询 status_url;running 时按 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 补全为绝对地址。最终结果只接受 http 或 https URL,并拒绝本机、内网及带凭据的地址。

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

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

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

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

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

402余额或额度不足

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

403缺少视频权限

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

409生成队列未启用

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

422模型或参数无效

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

500服务异常

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

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

同时发送 model=dola-30s、model_id=1、ai_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_id、ai_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 请求头。