
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_ppttask를 생성합니다. - 촘촘한 polling 대신 webhook으로 최종 결과를 받습니다.
- task 기록을 조회하고, 실행 중인 작업을 취소하고, 생성된 PPTX를 다운로드합니다.
예시는 Node.js를 사용합니다. 대부분의 Web 애플리케이션이 파일 업로드를 서버 라우트에서 처리하기 때문입니다. 동일한 HTTP 호출은 multipart와 JSON 요청을 보낼 수 있는 Go, Python, Ruby, Java 또는 다른 백엔드에서도 그대로 사용할 수 있습니다.
전체 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로 다시 패키징하세요. 이렇게 하면 변환기가 처리하기 쉬운 page-per-slide 입력이 됩니다.
좋은 입력:
- NotebookLM-style 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를 반환합니다. 이를 통해 업로드 엔드포인트가 무료 공개 파일 호스팅처럼 사용되는 것을 막습니다.
Step 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을 사용하세요.
Step 2: task 생성 전에 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의 첫 페이지입니다.
Step 3: pdf_to_ppt task 생성하기
통합 Task API로 작업을 생성합니다. 서버가 timeout 이후 재시도할 수 있다면 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입니다.
Step 4: webhook 수신하기
프로덕션에서는 브라우저 polling loop보다 webhook을 권장합니다. Codia는 task가 최종 상태에 도달하면 callback_url로 POST합니다.
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);
},
);event는 task_id를 기준으로 여러분의 데이터베이스에 저장하세요. 프론트엔드는 Codia API key를 노출하지 않고 여러분의 API를 통해 해당 row를 읽어 task UI를 갱신할 수 있습니다.
Step 5: 상태 조회 또는 기록 목록 보기
주요 흐름은 webhook으로 처리하더라도, status endpoint는 관리자 화면, 복구, task 기록에 유용합니다.
curl 'https://api.codia.ai/v1/open/tasks/550e8400-e29b-41d4-a716-446655440000' \
-H 'Authorization: Bearer {codia_api_key}'실행 중인 PDF to PPT task 목록:
curl 'https://api.codia.ai/v1/open/tasks?operation=pdf_to_ppt&status=pending,processing&limit=20' \
-H 'Authorization: Bearer {codia_api_key}'cursor pagination으로 기록 조회:
curl 'https://api.codia.ai/v1/open/tasks?operation=pdf_to_ppt&limit=20&after=1780028799' \
-H 'Authorization: Bearer {codia_api_key}'task 소유권은 task를 만든 API key에 묶입니다. 조회, 목록, 취소, webhook 대조에는 같은 key를 사용하세요.
Step 6: 취소와 부분 환불 처리
아직 pending 또는 processing 상태인 task는 취소할 수 있습니다.
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 task에서 10페이지가 이미 변환되었다면, 취소 시 65 credits가 환불되고 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
}
}task가 아직 pending이라면 예약된 credits가 전액 환불됩니다. succeeded task는 취소할 수 없습니다.
Step 7: 편집 가능한 PPTX 다운로드하기
task가 성공하면 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 route
아래의 최소 Express route는 브라우저 업로드를 받고 Codia task를 생성합니다. 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 검사, 사용자 소유권 검사, 데이터베이스 저장, 구조화된 retry 처리를 추가하세요.
운영 체크리스트
- 전달하기 전에 업로드 파일이 PDF인지 검증합니다.
CODIA_API_KEY와 webhook secret은 서버에 보관합니다.- UI에서 비용 미리보기가 필요하다면
/v1/open/estimate를 사용합니다. - task 생성 재시도 시
Idempotency-Key를 사용합니다. - 최종 상태 업데이트에는
callback_url을 우선 사용합니다. task_id, status,ppt_url, billing fields를 자체 데이터베이스에 저장합니다.- 기록 페이지에는
GET /v1/open/tasks의 cursor pagination을 사용합니다. - NotebookLM-style image-only PDF가 가장 적합하다는 점을 제품 UI에 명확히 표시합니다.
FAQ
업로드 대신 URL을 전달할 수 있나요?
네. PDF가 여러분의 스토리지에 호스팅되어 있다면 task 생성 시 input.pdf_url을 전달할 수 있습니다. 로컬 또는 비공개 PDF를 Codia를 통해 업로드했다면 input.upload_id를 사용하세요.
/v1/open/uploads는 공개 파일 URL을 반환하나요?
아니요. 공개 파일 URL이 아니라 불투명한 upload_id를 반환합니다. task service가 내부적으로 upload를 해석하고 user와 API key 기준으로 소유권을 검증합니다.
출력은 정말 편집 가능한가요?
목표는 텍스트, 레이아웃, 시각 요소가 재구성된 편집 가능한 PPTX입니다. 일부 밀도가 높거나 품질이 낮은 페이지는 image-only 또는 부분 편집 가능 상태로 나올 수 있습니다. task result에는 페이지별 status가 포함되므로 UI에서 어떤 페이지가 편집 가능한지 표시할 수 있습니다.
credits는 어떻게 청구되나요?
pdf_to_ppt는 변환된 페이지당 13 credits입니다. 실패한 task는 예약 credits가 환불됩니다. 취소된 PDF to PPT task는 아직 변환되지 않은 페이지 수만큼 환불됩니다.
polling이 필요한가요?
프로덕션의 주요 흐름에는 webhook을 사용하세요. GET /v1/open/tasks/{task_id}는 복구, 관리자 화면, 수동 새로고침에 적합합니다.