Introduction
The Danim API is an interface for reading and writing data from and to client online accounts, organizations that holds them, videos having been or being generated, and so on.
- https://api.danim.com/write/{DOMAIN}/{COMMAND} for writes
- https://api.danim.com/read/{DOMAIN}/{QUERY} for reads
Want to try our API?
Please contact us if you have any question or feature request!Content-Type
Note that when dealing with file(s) upload, you’ll will not be able to use application/x-www-form-urlencoded, but be compelled to use multipart/form-data instead. If you really want to avoid the multipart form when providing files, or for any other reason, you can provide them with URLs (of your own cloud, for instance) or preloaded-file IDs. You'll find more informations about that in the dedicated section.
Warning
When using Content-Type: multipart/form-data, don’t forget to provide the boundary (see details here). Some tools automatically calculate it (e.g. common browsers, Postman, etc.), but you could also provide an empty Content-Type header, and we’ll guess what you meant by this.
URL-encoded bodies
When sending URL-encoded data, set your HTTP Content-Type header to application/x-www-form-urlencodedand present your key/value pairs according to RFC-3986. For example, a video generation might look something like this:
-> POST /write/workspace/create
–––––
Authorization: ...
Content-Type: x-www-form-urlencoded
–––––
name=My+workspace&description=My+workspace+description
JSON-encoded bodies
But you may alternatively send your HTTP POST data with header Content-Type: application/json.There are some ground rules:
- you must explicitly set the Content-Type HTTP header to application/json. We won’t interpret your POST body as such without it.
- you cannot send your credentials as an attribute in your posted JSON.
- do not mix arguments up, between URL-encoded and JSON attributes. Choose one approach per request.
- providing an explicitly null value for an attribute will result in whichever default behavior is assigned to it.
-> POST /write/workspace/create
–––––
Authorization: ...
Content-Type: application/json
–––––
{
"name": "My workspace",
"description": "My workspace description"
}
Errors specific to JSON
If the posted JSON is invalid, you’ll receive one of the following errors (meta.type) in response (see response details below): In all cases, you’ll need to revise your JSON or how you’re transmitting your data to resolve the error condition.
Name | Description |
---|---|
InvalidJson | The JSON POST body cannot be parsed. This might be because it’s actually not JSON, or perhaps you did not correctly set your HTTP Content-Type header. Also make sure your JSON attribute keys are strings wrapped with double-quote characters. |
MalformedUtf8Characters | We understood that your code was JSON-like enough to parse it, but some character may have been incorrectly encoded. |
Evaluating responses
Warning
Do not forget to include treatment in your code for 40x and 50x responses, which like in all online protagonists could potentially occur.For success results, the data object property will contain useful pieces of information, such as the ID of a newly created entity, for example. Its internal fields are completely dependant on the endpoint. It is highly recommended that you log the meta.warnings on your side, to be alerted in real-time of any potential issues and deprecations.
<- 200 ✔
–––––
{
"success": true,
"data": {
"here_is": "some_useful_information",
"dedicated": "to_this_precise_endpoint"
},
"meta": {
"warnings": [
"First warning: beware on deprecation blablabla",
"Second warning: the data you see in data.here_is is blablabla"
]
}
}
For failure results, the message property will contain a detailed and verbose description of what had failed, and why, and how. There also will be a short machine-readable error code, found under meta.type, to get a more generic and clean way of dealing with errors (since this will be much more static in the course of time).
<- 200 ✖
–––––
{
"success": false,
"message": "Something dreadful occured when trying to generate video 5216c289-96d8-4124-bcf8-a70b7e688e97, it’s a complete shambles!",
"meta": {
"type": "VideoGenerationFailed"
}
}
Rate limits
Be aware of the fact that the amount of requests you may perform is limited in time (based upon your API credentials), with a precise per-minute quota.
This amount may be discussed, feel free to contact us.
If your request is overflowing the quota, you’ll get a failure (success: false) with meta.type: RateLimitExceeded(see response details above).
Authentication
We provide you app credentials, which consist of two keys, a public ID and a private secret. You must keep the secret out of sight. Go to API settings to get your credentials: app.danim.com/organization/api
Credentials
You have 3 ways to provide them in each request:- Extra non-standard X-foo-bar headers, the request containing two headers in the form of X-App-Id: <ID> and X-App-Secret: <secret>.
- Basic HTTP authentication, the request containing a header in the form ofAuthorization: Basic <credentials>, where credentials is the Base64 encoding of <ID>:<secret>(ID and secret joined by a single colon ":", see RFC 7617).
- Query parameters, in the form ofappId=<ID> and appSecret=<secret>.
Related errors
Name | Description |
---|---|
AuthenticationRequired | Missing app ID |
AppDoesNotExist | The app does not exist |
InvalidAppSecret | The provided app secret is not correct |
Examples
-> POST /write/video/create
–––––
Authorization: Basic base64encode(26rth97x368jj78n8nk3l4m:67t667thht7fwffhdullfuuejeh78ijjjf883zz2hhh34f)
-> POST /write/video/create
–––––
X-App-Id: 26rth97x368jj78n8nk3l4m
X-App-Secret: 67t667thht7fwffhdullfuuejeh78ijjjf883zz2hhh34f
-> POST /write/video/create?appId=26rth97x368jj78n8nk3l4m&appSecret=67t667thht7fwffhdullfuuejeh78ijjjf883zz2hhh34f
File sending
We herein illustrate the possible methods for providing files via your POST request body.
Preloaded file
With this method, you use the endpoint /upload/me to send a file and get its ID in the response. You can use this ID later in another POST request.See /upload/me dedicated section for more details.
⭐️ Best practice
We encourage the use of this method because it gives you the way to parallel loading on your frontend.If you have numerous file inputs in your form, using this method will save you from very long browser submits.
I.e. you load a file when the user fills the form (once loaded, you store the ID), and on submit, you send the ID instead of the user binary. Our backend will immediately assign the related file (given its ID).
Please note that the preloaded-file ID will be available and valid for the next 24 hours, and will be expired afterwards.
Binary and Content-Type: multipart/form-data header
-> POST /write/workspace/create
–––––
Authorization: ...
Content-Type: multipart/form-data; boundary=...
–––––
name: "My workspace"
logo: (binary)
Readable cloud URL
You could provide a remote file URL (of your own cloud, for instance), following the file+http(s):// format. For example, if you want to use the link https://my.cloud/path/file.jpg:-> POST /write/workspace/create
–––––
Authorization: ...
Content-Type: application/json
–––––
{
"name": "My workspace",
"logo": "file+https://my.cloud/path/file.jpg"
}
Why add file+?
Adding file+ to the scheme (http or https) is required, otherwise the field would be considered to be a mere string which should not be subject to any treatment, and would be understood as such. Please do not forget to send us the scheme, otherwise your url will not be accepted.Related errors
Name | Description |
---|---|
FileCannotBeDownloaded | Impossible to download a remote file given its URL |
FileMimeTypeCannotBeGuessed | File has been downloaded given its URL, but no MIME type could have been deduced |
FileExtensionCannotBeGuessed | File has been downloaded given its URL, but no extension could have been deduced |
FileIsTooLarge | File size is too high |
FileExtensionIsNotAccepted | File extension is not authorized (in general, on the platform) |
FileMimeTypeIsNotAccepted | File MIME type is not authorized (in general, on the platform) |
EmptyFile | The file has no content (size to 0) |
API Reference
/upload/me
This endpoint lets you upload files to our platform, which you may reuse elsewhere using their IDs (e.g., in the POST body of another API call).
Request parameters
The file content
{
"success": true,
"data": {
"file": {
"id": "d3f9c21c-9cb8-4f90-80b5-b87e1c951db3",
"created_at": "2020-09-05T15:40:40+00:00",
"original_name": "my-pic.jpeg",
"mime_type": "image/jpeg",
"size": 412941,
"url": "https://s3.eu-west-3.amazonaws.com/com.danim.prod/app/api-app/file-store/68147b15-d6ee-4a2c-bfa0-57242c4d2d97.jpeg",
"path": "app/api-app/file-store/68147b15-d6ee-4a2c-bfa0-57242c4d2d97.jpeg"
}
}
}
{
"success": false,
"message": "File extension \"myself.gif\" is not accepted. You can choose among: jpg, jpeg, png.",
"meta": {
"type": "FileExtensionIsNotAccepted"
}
}
Errors details
Error code | Error description |
---|---|
UserNotFound | The user is not found. |
CommandNotAuthorized | The command is blocked for you. This may have multiple possible reasons: payload content, API app rights, or even a bug... (feel free to contact us) |
BadParameter | Bad parameter in the payload |
MissingParameter | Missing parameter in the payload |
FileExtensionIsNotAccepted | File extension is not authorized |
FileIsTooLarge | File size is too high |
FileMimeTypeIsNotAccepted | File MIME type not authorized |
RateLimitExceeded | Rate limit exceeded. Contact us if you need more |
EmptyFile | The file has no content (size to 0) |
FileCannotBeDownloaded | File download failed |
FileExtensionCannotBeGuessed | File downloaded, but no extension could have been deduced |
FileMimeTypeCannotBeGuessed | File MIME type could not be guessed |
/write/video/change
This endpoint lets you change your video: name, description, etc.
Request parameters
The video ID
This is an object holding all the wished changes. Each sub-param of the patch can be omitted if you do not want to change it: this is equivalent to provide the value "IGNORE" (from the ImperativeVerb enum).
Name of the video (title which often appears on snippets, pages, etc.)
Description of the video (used for social sharing, locally on the platform, etc.)
{
"video_id": "eeeca184-e92c-46cf-b1bb-36703e91d765",
"patch": {
"name": "Wonderful house",
"description": "This video shows the rooms..."
}
}
{
"success": true,
"data": []
}
{
"success": false,
"message": "Video with ID \"e7aacd22-24fc-46fc-9752-5a5571944836\" cannot be found.",
"meta": {
"type": "VideoNotFound"
}
}
Errors details
Error code | Error description |
---|---|
VideoNotFound | The video is not found. |
CommandNotAuthorized | The command is blocked for you. This may have multiple possible reasons: payload content, API app rights, or even a bug... (feel free to contact us) |
BadParameter | Bad parameter in the payload |
MissingParameter | Missing parameter in the payload |
ConfigThumbnailNotFound | The config thumbnail is not found. |
StateChangeIsNotAuthorized | Changing video state from one to another is not authorized |
FileIsTooLarge | File size is too high |
FileMimeTypeIsNotAccepted | File MIME type not authorized |
InvalidTemplateDataFileFormat | Invalid file in template data |
InvalidTemplateDataFileMimeType | Invalid MIME type for file in template data |
MissingTemplateDataComposition | A required template composition has not been provided in template_data, when it should have been |
MissingTemplateDataInput | A required template input has not been provided in template_data, when it should have been |
FileExtensionIsNotAccepted | File extension is not authorized |
ApiAppVideoCannotBeLocal | Video made by an API app cannot be generated locally |
InsufficientCreditsForGeneration | The organization has no sufficient credits for this video generation |
InsufficientPlan | The organization plan does not allow your command. Please contact us if you think this is abnormal. |
RateLimitExceeded | Rate limit exceeded. Contact us if you need more |
/write/video/create
This endpoint lets you create a video from scratch. You may choose to set it as draft, or directly launch it for generation.
Request parameters
ID of template from which the video must be generated
Template data for generation (id/value pairs: see our guide). If there's no file (URL, ID, bin, etc.) in your data, you can provide a JSON-encoded string.
Description of the video
Name of the video
Workspace in which the video should be created
This webhook URL will be solicited on video generation success. It will by default use the POST method (Content-Type being application/x-www-form-urlencoded), and provide the following parameters:
- id (video ID)
- raw_url (full resolution video URL)
- raw_quicklook_url (GIF preview URL)
- raw_thumbnail_url (main thumbnail URL)
- player_url (video player URL)
If you prefer to get this stuff done via the GET method, add +get in your url scheme (e.g. https+get://my.app/webhook/1)
State of video. E.g., DRAFT to create a video that will not be generated once created, and that you may generate afterward via /write/video/generate; PENDING_IN_APP to start right now (queuing).
{
"template_id": "5958fb82-ef69-418f-abb1-5fa2ad355aaa",
"template_data": {
"text-1": "Alice's select value",
"text-2": "Alice's text value",
"image-1": "5c574215-5327-4d66-94b9-8f8b99720536"
},
"description": "Hi everyone, this video has an image! Alice.",
"name": "Alice's video",
"workspace_id": "36a167cf-8817-4f43-8b9d-9308ad25a8cc",
"callback_url": "https://lustkcehiqywpfarnbzmvxgdjo.service.com/mock/path",
"state": "GUESS"
}
{
"success": true,
"data": {
"id": "607e5f57-6e4f-4d28-bd81-377bb1a40de1",
"short_id": "fg56h378k",
"name": "Alice's video"
}
}
{
"success": false,
"message": "The input \"text-3\" is required but has not been provided",
"meta": {
"type": "MissingTemplateDataInput"
}
}
Errors details
Error code | Error description |
---|---|
TemplateNotFound | The template is not found. |
VideoAlreadyExists | Video already exists. |
WorkspaceInconsistency | Inconsistency between workspaces. |
WorkspaceNotFound | The workspace is not found. |
WorkspaceMustBeSet | A workspace ID must be provided |
CommandNotAuthorized | The command is blocked for you. This may have multiple possible reasons: payload content, API app rights, or even a bug... (feel free to contact us) |
BadParameter | Bad parameter in the payload |
MissingParameter | Missing parameter in the payload |
RateLimitExceeded | Rate limit exceeded. Contact us if you need more |
ConfigThumbnailNotFound | The config thumbnail is not found. |
EitherTemplateXorTemplateSourceMustBeSet | Either template xor template source must be set |
StateNotAuthorizedOnCreation | State not authorized on creation |
FileIsTooLarge | File size is too high |
FileMimeTypeIsNotAccepted | File MIME type not authorized |
InvalidTemplateDataFileFormat | Invalid file in template data |
InvalidTemplateDataFileMimeType | Invalid MIME type for file in template data |
MissingTemplateDataComposition | A required template composition has not been provided in template_data, when it should have been |
MissingTemplateDataInput | A required template input has not been provided in template_data, when it should have been |
FileExtensionIsNotAccepted | File extension is not authorized |
ImpossibleCompletion | Completion could not be made |
InvalidCompletionUrl | Provided URL is not valid for completion. |
InvalidHtmlCodeExtractedFromCompletionUrl | The HTML code extracted from the url is not valid for completion. |
StateChangeIsNotAuthorized | Changing video state from one to another is not authorized |
ApiAppVideoCannotBeLocal | Video made by an API app cannot be generated locally |
InsufficientCreditsForGeneration | The organization has no sufficient credits for this video generation |
InsufficientPlan | The organization plan does not allow your command. Please contact us if you think this is abnormal. |
EmptyFile | The file has no content (size to 0) |
FileCannotBeDownloaded | File download failed |
FileExtensionCannotBeGuessed | File downloaded, but no extension could have been deduced |
FileMimeTypeCannotBeGuessed | File MIME type could not be guessed |
TemplateDataFileNotFound | One provided file ID has been provided but cannot be found in old previous data. E.g., a preloaded-file ID has been provided, but was expired, so that we try to find the ID in old data files, with no success) |
/write/video/generate
This endpoint lets you launch video generation when this one is still a draft.
Request parameters
The video ID
{
"video_id": "9eef0d8c-7ff2-4e9d-96a0-eb5a93f04bd4"
}
{
"success": true,
"data": []
}
{
"success": false,
"message": "Video with ID \"e7aacd22-24fc-46fc-9752-5a5571944836\" cannot be found.",
"meta": {
"type": "VideoNotFound"
}
}
Errors details
Error code | Error description |
---|---|
BotNotFound | The bot is not found. |
CouldNotSendVideoGenerationSpecs | Could not send video generation specs to bot. |
VideoNotFound | The video is not found. |
MissingParameter | Missing parameter in the payload |
CommandNotAuthorized | The command is blocked for you. This may have multiple possible reasons: payload content, API app rights, or even a bug... (feel free to contact us) |
BadParameter | Bad parameter in the payload |
StateChangeIsNotAuthorized | Changing video state from one to another is not authorized |
FileIsTooLarge | File size is too high |
FileMimeTypeIsNotAccepted | File MIME type not authorized |
InvalidTemplateDataFileFormat | Invalid file in template data |
InvalidTemplateDataFileMimeType | Invalid MIME type for file in template data |
MissingTemplateDataComposition | A required template composition has not been provided in template_data, when it should have been |
MissingTemplateDataInput | A required template input has not been provided in template_data, when it should have been |
FileExtensionIsNotAccepted | File extension is not authorized |
ApiAppVideoCannotBeLocal | Video made by an API app cannot be generated locally |
InsufficientCreditsForGeneration | The organization has no sufficient credits for this video generation |
InsufficientPlan | The organization plan does not allow your command. Please contact us if you think this is abnormal. |
RateLimitExceeded | Rate limit exceeded. Contact us if you need more |
/write/video/remove
This endpoint lets you remove one of your videos.
Request parameters
The video ID
{
"video_id": "3947c1f6-fde1-490e-8320-6c2bcfc07f95"
}
{
"success": true,
"data": []
}
{
"success": false,
"message": "Video with ID \"e7aacd22-24fc-46fc-9752-5a5571944836\" cannot be found.",
"meta": {
"type": "VideoNotFound"
}
}
Errors details
Error code | Error description |
---|---|
VideoNotFound | The video is not found. |
CommandNotAuthorized | The command is blocked for you. This may have multiple possible reasons: payload content, API app rights, or even a bug... (feel free to contact us) |
BadParameter | Bad parameter in the payload |
FileExtensionIsNotAccepted | File extension is not authorized |
FileIsTooLarge | File size is too high |
FileMimeTypeIsNotAccepted | File MIME type not authorized |
MissingParameter | Missing parameter in the payload |
RateLimitExceeded | Rate limit exceeded. Contact us if you need more |
/read
Register your app credentials to start exploring our API.
xxxxxxxxxx
query GetVideosByState($quantity: Int = 5, $state: VideoState = DONE) {
videos(first: $quantity, state: $state) {
edges {
node {
id
name
shortId
}
}
}
}
xxxxxxxxxx
{
"quantity": 5,
"state": "DONE"
}
Guides
Read a template and create a video
Each template has a different structure. You need to know which data is needed by Danim for each template, which fields are required or not, the ids of these fields, etc.
You can find these informations by two different ways:
Easy way: with danim.com
You can find out how to write your video creation request on each generation page by clicking on the "code" icon below the template thumbnail.
Hard way: with GraphQL
query GetTemplateSpecsById($id: String = null) {
template(id: $id) {
specs
inputs
}
}
Warning
You have to parse specs in GraphQL response because this attribute is stringified.{
"specs": {
"info": {
"ratios": [1, 1.78, 0.56],
"theme": {
"layout": {
"main-color": {
"name": "Main color"
},
"secondary-color": {
"name": "Secondary color"
},
"text-color": {
"name": "Text color"
}
},
"variants": [
{
"name": "Main theme",
"colors": {
"main-color": "#00aaa1",
"secondary-color": "#09121d",
"text-color": "#FFFFFF"
}
},
{
"name": "Alternative theme",
"colors": {
"main-color": "#f1a649",
"secondary-color": "#3b24ab",
"text-color": "#FFFFFF"
}
}
]
}
},
"compositions": [
{
"id": "01-intro",
"description": "Introduction",
"required": false,
"texts": [
{
"id": "title",
"description": "Film title",
"required": true,
"type": "text",
"max_length": 30,
"strict_check": false
},
{
"id": "duration",
"description": "Film duration",
"required": false,
"type": "number",
"strict_check": true,
"is_int": true,
"min_numeric_value": 0,
"suffix": "minutes"
}
]
},
{
"id": "02-review",
"description": "Review",
"required": true,
"texts": [
{
"id": "score",
"description": "Film score",
"required": true,
"type": "number",
"strict_check": true,
"is_int": false,
"min_numeric_value": 0,
"max_numeric_value": 5
}
],
"files": [
{
"id": "poster",
"description": "Poster",
"required": false,
"type": "image",
"min_width": 500,
"min_height": 1200
}
]
},
{
"id": "03-casting",
"description": "Casting",
"required": false,
"is_dynamic": true,
"min_dynamic_zone_quantity": 0,
"max_dynamic_zone_quantity": 5,
"dynamic_zone_start": true,
"texts": [
{
"id": "job",
"description": "Job / Role",
"required": false,
"type": "text",
"max_length": 20,
"strict_check": false
},
{
"id": "casting-name",
"description": "Person name",
"required": true,
"type": "text",
"max_length": 20,
"strict_check": false
}
],
"files": [
{
"id": "bg-footage",
"description": "Background image / video",
"required": false,
"type": "image_video",
"min_width": 1080,
"min_height": 1080,
"duration_strategy": "variable",
"min_duration": 4,
"max_duration": 30,
"force_muted": true
}
]
},
{
"id": "04-quote",
"description": "Quote",
"required": false,
"is_dynamic": true,
"dynamic_zone_end": true,
"texts": [
{
"id": "quote-paragraph",
"description": "Quote paragraph",
"required": true,
"type": "paragraph",
"max_paragraph_line_length": 20,
"max_paragraph_lines": 3,
"strict_check": true
}
]
}
],
},
"inputs": [
"title",
"duration",
"score",
"poster",
"job",
"casting-name",
"bg-footage",
"quote-paragraph"
]
}
How to understand specs?
One of the most important rules for compositions, texts and files is required. In some cases, you can omit an input during the creation of a video and it will not be visible in the video. In other cases, you can't and an error will be thrown.
Here is a summary:
- you didn’t provide any input (required or optional) of an optional composition: the composition itself will not appear in the video
- you didn't provide the value of an optional input in a required (or optional) composition: its value will then be empty
- you didn't provide the value of a required input in a required composition or in an optional composition from which you provided another input: an error will be thrown (see errors)
Please note that these rules are informative in most of the cases. For example, if you don't respect the rule max_length for a text input, no error is returned unless strict_check is TRUE. These are the rules affected by strict_check:
- max_length
- min_numeric_value
- max_numeric_value
- is_int
- max_paragraph_line_length
- max_paragraph_lines
- values
- For texts: text, number, paragraph, select, boolean
- For files: image, video, image_video
File sending
We'll use the template specs above as an example. The field called poster is a file, so you have to choose between the several ways described in file sending section to send it.
We highly advise you to send your file(s) separately with /upload/me and get the file id in response with data.file.id.
Images have rules related to their dimensions: min_width, min_height, max_width, max_height.
These rules are informative, so if your image doesn't match the required dimensions, we'll automatically crop and resize it: no error will be thrown. You can ask us to crop these images by sending an additional crop object (see expert mode below).
Videos have rules related to their duration: duration_strategy ("fixed" or "variable"), duration, min_duration, max_duration. If duration_strategy is "fixed", video duration is defined by duration. If duration_strategy is "variable", video duration has to be defined between min_duration and max_duration.
As for images, these rules are informative, so if your video doesn't match the specified duration, we'll automatically cut it: no error will be thrown. You can ask us to cut these videos by sending an additional range array (see expert mode below).
Launch a new generation
template_data can have 2 different layouts depending on the features you want to use. A minimalist and easy to use mode allows you to create a video without having to set the ratio and the theme. In this mode, you can't duplicate the compositions or define custom crop and cut values for images and videos. All you have to do is to send a list of entries you want to see in your video.
{
"template_data": {
"title": "A great movie",
"duration": 112,
"score": 4.8,
"poster": "my-file-id"
}
}
{
"template_data": {
"score": 4.8
}
}
{
"template_data": {
"duration": 112,
"poster": "my-file-id"
}
}
{
"template_data": {
"title": "A great movie",
"duration": 20.54,
"score": -2,
"poster": "my-file-id"
}
}
null, false, "" or undefined?
The easiest way to tell Danim to ignore an input is to not send this input in template_data. You can also send null, but if you send an empty string "" or false, the value of the input will be evaluated.{
"template_id": "your-template-id",
"template_data": {
"title": "A great movie",
"duration": 112,
"score": 4.8,
"poster": "my-file-id"
},
"name": "My favorite film",
"callback_url": "https://my.app/webhook"
}
{
"success": true,
"data": {
"id": "new-video-long-id",
"short_id": "new-video-short-id",
"name": "My favorite film"
}
}
Be notified with a webhook
These attributes are sent: id, raw_url (full resolution video), raw_quicklook_url (GIF preview), raw_thumbnail_url (main thumbnail), and player_url.
This is a common use case of callback_url:
- Save data.id from /write/video/create response in your database
- When a request is received at your callback_url, find the related video in your database thanks to the id in POST parameters
- You're now sure that this video has been created. You can save data sent by the webhook in your database, or get additional informations with another GraphQL request
Quick tip
During your tests with our API, we highly suggest that you use webhook.site to try the callback_url attribute.