Production API 概览
HTTP API 暴露与 MYEQ Studio 相同的 provider-neutral 制作权限。
文档页面是公开的,但 API/MCP 能力是否可用由实时产品 Manifest 和账户权限决定。foundation / experimental 不等于稳定承诺。
一个生命周期
Context → Plan → Preflight → Execute → Inspect → Decide → Repair → Certify → Deliver 是长期语义模型。使用前读取实时 Manifest。
先规划,再付费
Plan 在不触发付费生成的情况下产出可检查提案;Preflight 验证访问、参考、能力和预算。
curl -X POST https://myeq.pro/api/v1/production/plans \
-H "Authorization: Bearer $MYEQ_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"projectId": "project-123",
"script": "Create a 60-second vertical trailer",
"vertical": "ai_film",
"sourceLanguage": "en",
"responseLanguage": "es",
"dialogueLanguage": "es",
"visualPromptLanguage": "en"
}'执行是持久的
已接受工作由 MYEQ 持续负责,客户端断开后仍可通过 Job 查询。
公共合约保持 provider-neutral
客户端表达制作意图和 MYEQ 能力,而不是选择私有 runtime。
Studio、API 和 MCP 的 24 / 30 / 60 FPS
三个接口使用同一份已确认的场景 snapshot 和准入检查。保留请求的 FPS,不会自动回退到 24 FPS。可用性取决于已批准的 worker 和场景的准确条件。
START/END 画布为 704×1280、1280×704 或 960×960。I2V 可生成音频或无音频;A2V 要求有使用权限的源音频,并保留所选起始偏移。本表不代表 ingredients 或 V2V 支持高 FPS。seed 必须是 0–2147483647 的整数。 I2V 和 A2V 在已部署服务明确准入时,支持 24、30、60 FPS 的仅 END 输入。END 引导最后一帧,开头根据提示词生成。保持 start 为空,将图片放在 end 中。这不保证像素完全一致,也不会倒放视频。 A2V 始终需要有使用权限的源音频,以及至少一张 START 或 END 图片;不支持仅音频生成。
| FPS | 可准入类别 | 物理限制 |
|---|---|---|
| 24 | 默认值;所选模态仍需通过自身检查 | 时长、音频和参考取决于所选原生类别 |
| 30 | 仅限已批准的 I2V / A2V | 720p · 2–10 整秒 · 9:16 / 16:9 / 1:1 · I2V/A2V:START、END 或两者;A2V 还需要源音频 |
| 60 | 仅限已批准的 I2V / A2V | 720p · 2–10 整秒 · 9:16 / 16:9 / 1:1 · I2V/A2V:START、END 或两者;A2V 还需要源音频 |
保存、核对报价,再确认一个场景
创建或读取有权限的项目,通过 PATCH expectedVersion 保存场景,再将相同的 snapshot 和 targetSceneId 发送至 /api/v1/production/snapshots/preflight-scene。检查 data.ready、issues 和 estimatedCredits;此调用不创建 job 或预留积分。使用 confirm=true 和固定 Idempotency-Key 调用 /api/v1/production/snapshots/generate-scene,然后查询返回的 jobId。MCP 使用 myeq.video.preflight_scene 和 myeq.video.generate_scene,付费提交绝不自动重试。场景设置覆盖全局默认值;保留匹配的已确认 sceneContracts。
旧 /video/start、/video/create、/video/create-job 和 /video/preflight 已退役:默认/24 返回 410;30/60 返回 422,要求迁移到 snapshot。25/48/50 不受支持。preflight 失败不会更改 FPS 或收费。检查成功也不预留可用资源或账户余额。
const base = process.env.MYEQ_BASE_URL || 'https://myeq.pro';
const headers = { Authorization: 'Bearer ' + process.env.MYEQ_API_KEY, 'Content-Type': 'application/json' };
async function api(path, method, body, extra = {}) {
const response = await fetch(base + path, { method, headers: { ...headers, ...extra },
...(body === undefined ? {} : { body: JSON.stringify(body) }) });
const value = await response.json();
if (!response.ok) throw new Error(JSON.stringify(value));
return value;
}
const project = await api('/api/v1/projects', 'POST', { title: '60 FPS', aspectRatio: '9:16', duration: 4 });
const sourceAudioUrl = process.env.SOURCE_AUDIO_URL;
const generationInputs = { videoProvider: 'myeq_v2', videoModelSelection: 'myeq_v2',
inputModality: sourceAudioUrl ? 'audio_to_video' : 'image_to_video', durationSeconds: 4,
fps: 60, outputResolution: '720p', includeAudio: Boolean(sourceAudioUrl), seed: 12345,
...(sourceAudioUrl ? { sourceAudioUrl, sourceAudioStartTime: 0 } : {}) };
const scene = { id: 'scene-1', prompt: 'A slow camera move. Preserve the subject.',
settings: { fps: 60, durationSeconds: 4 },
start: process.env.START_URL ? [{ name: 'START', url: process.env.START_URL }] : [],
end: process.env.END_URL ? [{ name: 'END', url: process.env.END_URL }] : [], context: [], avatar: [] };
const snapshot = { id: crypto.randomUUID(), version: 1, projectId: project.id,
ownerId: project.ownerUserId, sourceEditorStateVersion: project.version + 1,
createdAt: new Date().toISOString(), projectTitle: project.title,
aspectRatio: '9:16', resolution: '720p', durationSeconds: 4, fps: 60,
generationInputs, scenes: [scene], avatars: [], assetsSummary: [],
sourceAudio: sourceAudioUrl ? { url: sourceAudioUrl, durationSeconds: null } : null, provenance: { source: 'ui' } };
await api('/api/v1/projects/' + project.id, 'PATCH', { expectedVersion: project.version,
editorState: { ...project.editorState, generationInputs,
sceneStoryboard: { scenes: [scene] }, productionSnapshot: snapshot } });
const input = { snapshot, targetSceneId: scene.id };
const quote = await api('/api/v1/production/snapshots/preflight-scene', 'POST', input);
console.log(quote);
if (quote.data?.ready && process.env.CONFIRM_PAID === 'true') {
const idempotencyKey = process.env.SAVED_IDEMPOTENCY_KEY;
if (!idempotencyKey) throw new Error('Persist one stable key before submitting');
const result = await api('/api/v1/production/snapshots/generate-scene', 'POST',
{ ...input, confirm: true }, { 'Idempotency-Key': idempotencyKey });
console.log(result);
const jobId = result.data?.pendingTake?.orchestrationJobId || result.data?.asset?.metadata?.jobId;
if (jobId) console.log(await api('/api/v1/production/jobs/' + encodeURIComponent(jobId), 'GET'));
}