Publishes your own eBook PDF in three steps: signed URL, PUT to S3 and confirmation.
Upload an eBook PDF
Publishes an eBook PDF you produced yourself, instead of generating the content with AI. The file does not go through the Panda API: you request a signed S3 URL, send the file straight to it, and then confirm.
Every call on this page uses the POST /aiworkflow/aipackage route, varying the action field in the body.
AuthorizationCalls to the Panda API require the API key in the
Authorizationheader, without theBearerprefix. ThePUTof the file to the signed URL does not carry your API key — the authorization is already embedded in the URL.
The three steps
| # | Call | What happens |
|---|---|---|
| 1 | POST /aiworkflow/aipackage with action: upload_abstract_url | Returns the signed URL and the file key |
| 2 | PUT of the PDF straight to the returned URL | The file reaches S3 |
| 3 | POST /aiworkflow/aipackage with action: confirm_abstract | The eBook is published in that language |
Step 1 — request the signed URL
Body:
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | upload_abstract_url |
video_id | string (uuid) | Yes | Video the eBook belongs to |
lang | string | Yes | Language of the eBook (e.g. pt-BR) |
{ "action": "upload_abstract_url", "video_id": "{video_id}", "lang": "pt-BR" }Response (200):
{ "url": "https://...", "key": "...", "expires_in": 3600 }| Field | Type | Content |
|---|---|---|
url | string | Signed S3 URL — this is where you PUT the file |
key | string | Path of the object in storage |
expires_in | integer | Validity of the URL in seconds (3600 = 1 hour) |
The field isurl, notupload_url.This has already happened in production: the client read
upload_url, gotundefined, sent thePUTto the wrong host, received a 200 from a server that was not S3, and only found out at step 3, which failed with "Uploaded PDF not found". A200at step 2 does not prove the file reached the right place — make sure the URL you use is exactly the one returned inurl.
The URL expires in 1 hour (expires_in: 3600). If the upload takes longer than that, request a new one before retrying.
Step 2 — send the file
PUT the PDF straight to the URL from step 1, without the Authorization header:
curl -X PUT "{url_from_step_1}" \
-H "Content-Type: application/pdf" \
--data-binary "@my-ebook.pdf"Step 3 — confirm and publish
Body:
| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | confirm_abstract |
video_id | string (uuid) | Yes | The same video from step 1 |
lang | string | Yes | The same language from step 1 |
upload_success | boolean | Yes | true to publish the uploaded file |
{ "action": "confirm_abstract", "video_id": "{video_id}", "lang": "pt-BR", "upload_success": true }After the confirmation, the eBook shows up in config.ai.resources[] in GET /videos/{video_id}, with type: abstract and the srclang of the language you sent.
If step 2 did not put the file on the signed URL, step 3 fails with "Uploaded PDF not found". Start over from step 1.
eBook images
Two actions of the same route return a signed URL for images. The flow is the same: request the URL, PUT the file, and use the key that comes back.
upload_logo_url — cover logo
upload_logo_url — cover logo| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | upload_logo_url |
video_id | string (uuid) | Yes | Video |
content_type | string | Yes | MIME type of the file (e.g. image/png) |
The returned key is the value of logo_key in abstract_config — see Generate an AI Resource.
upload_abstract_image_url — image inserted in the editor
upload_abstract_image_url — image inserted in the editor| Field | Type | Required | Description |
|---|---|---|---|
action | string | Yes | upload_abstract_image_url |
video_id | string (uuid) | Yes | Video |
lang | string | Yes | Language of the eBook |
content_type | string | Yes | MIME type of the file (e.g. image/jpeg) |
To generate an image with AI instead of uploading your own, see Generate an AI Image.
Example Usage
cURL — full flow
# 1) request the signed URL
curl -X POST "https://api-v2.pandavideo.com.br/aiworkflow/aipackage" \
-H "Authorization: {api_key}" \
-H "Content-Type: application/json" \
-d '{ "action": "upload_abstract_url", "video_id": "{video_id}", "lang": "pt-BR" }'
# 2) send the file to the URL returned in "url" (no Authorization)
curl -X PUT "{url}" \
-H "Content-Type: application/pdf" \
--data-binary "@my-ebook.pdf"
# 3) confirm
curl -X POST "https://api-v2.pandavideo.com.br/aiworkflow/aipackage" \
-H "Authorization: {api_key}" \
-H "Content-Type: application/json" \
-d '{ "action": "confirm_abstract", "video_id": "{video_id}", "lang": "pt-BR", "upload_success": true }'