
NotebookLM-style 幻灯导出的价值在于:每一页已经是一张完整的视觉幻灯片。问题也在这里:PDF 页面通常是扁平页面,不是 PowerPoint 的对象模型。Codia Open API 的 pdf_to_ppt task 面向的正是这个场景:输入是 NotebookLM-style PDF,也就是每页都是整页图片、扫描幻灯或截图型页面;输出是一份可编辑的 .pptx。
这篇文章会从外部开发者角度,走完整个生产级接入流程:
- 用
/v1/open/uploads上传本地或私有 NotebookLM-style PDF。 - 创建任务前先预估 credits。
- 通过统一 Task API 创建
pdf_to_ppt任务。 - 用 webhook 接收最终结果,而不是在浏览器里高频轮询。
- 查询历史任务、取消任务,并下载生成的 PPTX。
示例使用 Node.js,因为大多数 Web 应用都会通过服务端路由处理文件上传。同样的 HTTP API 也适用于 Go、Python、Ruby、Java 或任何能发送 multipart 和 JSON 请求的后端。
查看完整 endpoint schema、请求/响应字段和实时 OpenAPI 规范时,可以配合打开 Codia API Reference。
什么是 NotebookLM-style PDF
当前 PDF to PPT 流水线最适合 image-only PDF:每个 PDF 页面都应该是一张整页图片。这包括 NotebookLM-style 导出的资料页、截图型幻灯片、扫描报告,以及其他静态演示视觉。
它不适合复杂文本流 PDF,也不适合需要把段落、矢量图、表格和内嵌对象当作文档版式来解析的文件。如果你的源文件是普通文本 PDF,建议先把每页渲染成高分辨率 PNG 或 JPEG,再把这些图片重新打包成 image-only PDF。这样转换器拿到的就是干净的一页一张幻灯输入。
适合的输入:
- NotebookLM-style PDF 导出
- 扫描或截图型幻灯片
- 一页 PDF 对应一张幻灯的 image-only PDF
- 需要变成可编辑 PPTX 的静态演示页面
风险较高的输入:
- 带连续段落的长文档 PDF
- 矢量对象很多、层级复杂的报告
- 有些页是文档、有些页是幻灯的混合 PDF
- 小字不可读的低分辨率扫描件
架构
Codia API key 应该只放在服务端。浏览器把 PDF 上传到你的后端,你的后端再把 PDF 上传一次到 Codia,后续所有调用都使用返回的不透明 upload_id。
Browser
-> your server: multipart PDF upload
-> Codia /v1/open/uploads: multipart PDF upload
<- upload_id
-> Codia /v1/open/estimate: JSON with upload_id
-> Codia /v1/open/tasks: JSON with upload_id and callback_url
<- task_id
Codia
-> your webhook: terminal task event
Browser
-> your server: read task/result
-> download ppt_url when succeeded/v1/open/uploads 不返回公网文件 URL,只返回和同一 user、同一 API key 绑定的 upload_id,并带短期有效期。这样不会把上传接口变成免费的公网文件托管。
第 1 步:上传 PDF,拿到 upload_id
本地或私有 PDF 先用 multipart/form-data 发到 /v1/open/uploads。
curl 'https://api.codia.ai/v1/open/uploads' \
-H 'Authorization: Bearer {codia_api_key}' \
-F 'file=@./notebooklm-briefing.pdf'响应示例:
{
"code": 0,
"message": "ok",
"data": {
"upload_id": "upl_550e8400-e29b-41d4-a716-446655440000",
"filename": "notebooklm-briefing.pdf",
"size": 1234567,
"content_type": "application/pdf",
"expires_at": 1764086400
}
}如果文件由 Codia 上传管理,任务里传 upload_id。只有当 PDF 托管在你自己的存储里,并且 Codia 可以访问时,才传 pdf_url。
第 2 步:创建任务前预估 credits
pdf_to_ppt 按页计费,每转换一页 13 credits。如果你传了 page_no,预估值就是所选页数。如果不传 page_no,服务会根据 PDF 解析页数。
curl 'https://api.codia.ai/v1/open/estimate' \
-H 'Authorization: Bearer {codia_api_key}' \
-H 'Content-Type: application/json' \
--data '{
"operation": "pdf_to_ppt",
"input": {
"upload_id": "upl_550e8400-e29b-41d4-a716-446655440000",
"page_no": [0, 1, 2]
}
}'响应示例:
{
"code": 0,
"message": "ok",
"data": {
"operation": "pdf_to_ppt",
"page_count": 3,
"unit": "13 credits per page",
"credits": 39,
"available_credits": 120
}
}页码是 0-based。0 表示 PDF 第一页。
第 3 步:创建 pdf_to_ppt 任务
使用统一 Task API 创建任务。如果你的服务端可能因为超时重试,请带上 Idempotency-Key。
curl 'https://api.codia.ai/v1/open/tasks' \
-H 'Authorization: Bearer {codia_api_key}' \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: notebooklm-briefing-2026-05-29' \
--data '{
"operation": "pdf_to_ppt",
"input": {
"upload_id": "upl_550e8400-e29b-41d4-a716-446655440000",
"page_no": [0, 1, 2],
"title": "NotebookLM source briefing"
},
"callback_url": "https://your-app.example.com/webhooks/codia-task"
}'响应示例:
{
"code": 0,
"message": "ok",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"operation": "pdf_to_ppt",
"status": "pending",
"created_at": 1780028799
}
}pending 表示任务已入队,processing 表示 worker 已经开始处理。终态是 succeeded、failed、canceled。
第 4 步:接收 webhook
生产环境建议用 webhook,而不是让浏览器高频轮询。任务进入终态后,Codia 会 POST 到 callback_url。
import crypto from 'node:crypto';
import express from 'express';
const app = express();
app.post(
'/webhooks/codia-task',
express.raw({ type: 'application/json' }),
(req, res) => {
const signature = String(req.header('X-Codia-Signature') || '');
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.CODIA_WEBHOOK_SECRET!)
.update(req.body)
.digest('hex');
const ok =
signature.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
if (!ok) {
res.sendStatus(401);
return;
}
const event = JSON.parse(req.body.toString('utf8'));
if (event.operation === 'pdf_to_ppt' && event.status === 'succeeded') {
console.log('PPTX ready:', event.result?.ppt_url || event.ppt_url);
}
if (event.status === 'failed') {
console.error('Conversion failed:', event.error_code, event.error);
}
res.sendStatus(200);
},
);建议把 webhook event 按 task_id 写入你自己的数据库。前端只读你的业务 API,不需要接触 Codia API key。
第 5 步:查询状态或历史任务
主流程由 webhook 推动,但状态接口仍然适合后台管理、异常恢复和历史列表。
curl 'https://api.codia.ai/v1/open/tasks/550e8400-e29b-41d4-a716-446655440000' \
-H 'Authorization: Bearer {codia_api_key}'查询正在运行的 PDF to PPT 任务:
curl 'https://api.codia.ai/v1/open/tasks?operation=pdf_to_ppt&status=pending,processing&limit=20' \
-H 'Authorization: Bearer {codia_api_key}'用 cursor 分页查询历史任务:
curl 'https://api.codia.ai/v1/open/tasks?operation=pdf_to_ppt&limit=20&after=1780028799' \
-H 'Authorization: Bearer {codia_api_key}'任务归属按创建它的 API key 隔离。查询、列表、取消和 webhook 对账都应该使用同一把 key。
第 6 步:取消任务和部分退款
仍处于 pending 或 processing 的任务可以取消:
curl -X POST 'https://api.codia.ai/v1/open/tasks/550e8400-e29b-41d4-a716-446655440000/cancel' \
-H 'Authorization: Bearer {codia_api_key}'对 pdf_to_ppt 来说,取消只退还尚未完成转换的页。如果一个 15 页 NotebookLM-style PDF 任务已经完成 10 页转换,取消后退 65 credits,已完成的 10 页按 130 credits 计费。
{
"code": 0,
"message": "ok",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"operation": "pdf_to_ppt",
"status": "canceled",
"credits_reserved": 195,
"credits_charged": 130,
"credits_refunded": 65
}
}如果任务还在 pending,则全额退还。已经 succeeded 的任务不能取消。
第 7 步:下载可编辑 PPTX
任务成功后读取 result.ppt_url,及时下载文件。
{
"code": 0,
"message": "ok",
"data": {
"task_id": "550e8400-e29b-41d4-a716-446655440000",
"operation": "pdf_to_ppt",
"status": "succeeded",
"progress": 100,
"result": {
"ppt_url": "https://static.codia.ai/pptx/notebooklm-source-briefing.pptx",
"page_count": 3,
"pages": [
{
"index": 0,
"source_page": 0,
"editable": true,
"status": "editable",
"preview_url": "https://static.codia.ai/previews/page-0.png"
}
]
}
}
}PPTX 是最终交付物。你可以把它保存到自己的系统、挂到用户项目里,或交给 PowerPoint、Keynote、Google Slides 工作流继续处理。
完整 Node.js 路由
下面这个最小 Express 路由接收浏览器上传,并创建 Codia 任务。Codia API key 保持在服务端,不会发送到浏览器。
import express from 'express';
import multer from 'multer';
import FormData from 'form-data';
import crypto from 'node:crypto';
const app = express();
const upload = multer({ storage: multer.memoryStorage() });
const CODIA_API_BASE = 'https://api.codia.ai';
app.post('/api/notebooklm-pdf-to-pptx', upload.single('file'), async (req, res) => {
if (!req.file) {
res.status(400).json({ error: 'file is required' });
return;
}
const form = new FormData();
form.append('file', req.file.buffer, {
filename: req.file.originalname || 'notebooklm.pdf',
contentType: req.file.mimetype || 'application/pdf',
});
const uploadRsp = await fetch(`${CODIA_API_BASE}/v1/open/uploads`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CODIA_API_KEY}`,
...form.getHeaders(),
},
body: form as any,
}).then((r) => r.json());
if (uploadRsp.code !== 0) {
res.status(502).json(uploadRsp);
return;
}
const uploadId = uploadRsp.data.upload_id;
const idempotencyKey = crypto.randomUUID();
const taskRsp = await fetch(`${CODIA_API_BASE}/v1/open/tasks`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.CODIA_API_KEY}`,
'Content-Type': 'application/json',
'Idempotency-Key': idempotencyKey,
},
body: JSON.stringify({
operation: 'pdf_to_ppt',
input: {
upload_id: uploadId,
title: req.body.title || req.file.originalname?.replace(/\.pdf$/i, ''),
},
callback_url: `${process.env.PUBLIC_APP_URL}/webhooks/codia-task`,
}),
}).then((r) => r.json());
if (taskRsp.code !== 0) {
res.status(502).json(taskRsp);
return;
}
res.status(202).json({
task_id: taskRsp.data.task_id,
status: taskRsp.data.status,
upload_id: uploadId,
});
});真实生产代码还需要增加请求大小限制、MIME 校验、用户归属校验、数据库持久化和结构化重试。
上线检查清单
- 转发前先校验上传文件是 PDF。
CODIA_API_KEY和 webhook secret 只放服务端。- UI 需要价格预览时,先调用
/v1/open/estimate。 - 任务创建发生重试时使用
Idempotency-Key。 - 主流程用
callback_url接收终态更新。 - 在自己的数据库里保存
task_id、状态、ppt_url和计费字段。 - 历史任务页使用
GET /v1/open/tasks的 cursor pagination。 - 在产品文案里明确说明:NotebookLM-style image-only PDF 效果最好。
常见问题
可以传 URL,不上传文件吗?
可以。如果 PDF 托管在你自己的存储里,创建任务时传 input.pdf_url。本地或私有 PDF 通过 Codia 上传时,传 input.upload_id。
/v1/open/uploads 会返回公网文件 URL 吗?
不会。它返回不透明 upload_id,不是公网文件 URL。任务服务会在服务端解析 upload,并按 user 和 API key 校验归属。
输出真的可编辑吗?
目标是输出带重建文本、版式和视觉元素的可编辑 PPTX。部分密集或低质量页面可能是 image-only 或部分可编辑。任务结果会返回 per-page status,方便你的 UI 展示哪些页可编辑。
credits 怎么计费?
pdf_to_ppt 每转换一页消耗 13 credits。失败任务退还 reserved credits。取消中的 PDF to PPT 任务只退还还没有转换完成的页。
还需要轮询吗?
生产主流程建议用 webhook。GET /v1/open/tasks/{task_id} 更适合异常恢复、后台管理和手动刷新。