Upload a file
Upload an image or a video as an Asset, or a file as a Brand Document. Then register an Asset in the library of an advertiser.
The bytes of a large file never go through the API. You reserve the file and send the bytes to a storage URL. A Document then needs a finalize call. An Asset needs none: a read of the Asset completes it.
- An Asset is an image or a video that goes into an ad. You register it in the library of an advertiser.
- A Document is reference material on a Brand, such as a logo or a guidelines PDF. An agent reads it. It never leaves AdCrunch.
Upload a small Asset in one call
Send the file as the raw body of POST /assets. The answer is the Asset, with the status ready.
curl --data-binary @ad.png \
-H "Authorization: Bearer $ADCRUNCH_API_KEY" \
"https://api.adcrunch.dev/assets?filename=ad.png"
filenameis required.nameis optional and replaces the filename in the display.- Do not use
multipart/form-data. The body is the file. - The request needs a
Content-Length. Without one, the answer is411length_required. - The route takes a body up to 95 MB. Over that, the answer is
413use_upload_url: use the two steps below. A body over the limit of the Cloudflare plan of the domain, 100 MB at the lowest, gets a413from Cloudflare with noerrorcode. - AdCrunch identifies the type from the bytes. A refused file leaves no object and no Asset. The answer is
413too_largeor415unsupported_typeorunreadable.
Import an Asset from a URL
Send a JSON body to POST /assets. AdCrunch fetches the file from the URL. The answer is 202 with the Asset, with the status pending.
curl -X POST https://api.adcrunch.dev/assets \
-H "Authorization: Bearer $ADCRUNCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "sourceUrl": "https://example.com/ad.png" }'
sourceUrlis required, and it must be anhttpsURL. Another scheme answers400invalid_url, and AdCrunch creates nothing.nameis optional.- The host must be a public name. An IP address, a local or private host, or an AdCrunch domain (
adcrunch.devand its subdomains) answers400source_not_allowed. To store a file that is on an AdCrunch domain, send its bytes instead. - The source must send a
Content-Length. A source that declares a type AdCrunch does not accept, such asimage/svg+xmlortext/html, fails at once withunsupported_type. AdCrunch follows a redirect only to anhttpsURL. - Read the Asset with
GET /assets/{id}until its status isreadyorfailed. - A failure is on the Asset, not in the answer.
failureCodeissource_unreachable(the source did not answer, answered with an error, or sent the file in part),source_length_unknown(the source sent noContent-Length),too_large,unsupported_typeorunreadable. - The file takes its name from the
Content-Dispositionof the source, or else from the last segment of the URL.
Upload an Asset in two steps
Reserve the Asset
curl -X POST https://api.adcrunch.dev/assets/uploads \
-H "Authorization: Bearer $ADCRUNCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "filename": "spring-sale.mp4" }'The answer holds the id of the Asset, the storage URL, and the time when the URL stops working:
{
"assetId": "ast_7c1e9a",
"uploadUrl": "https://…",
"expiresAt": 1790000000000
}Send the bytes to the storage URL
curl -X PUT "$UPLOAD_URL" \
-H "If-None-Match: *" \
--upload-file spring-sale.mp4The PUT of an Asset needs the header If-None-Match: *. A PUT without it answers 403. The URL can create the file once, and it can never replace it:
- The first
PUTanswers200. - A second
PUTanswers412. A412means that the file is already stored, for example after a retry when the first answer was lost. Continue to the next step.
The bytes of an Asset never change. To use a different file, reserve a new Asset.
The PUT of an Asset needs no Content-Type. AdCrunch identifies the type of an Asset from its bytes, and a Content-Type is only a hint. A Document still needs the Content-Type of its file.
Do not send your API key to this URL. The URL itself gives write access: each person who holds it can create that one file until the URL expires.
Read the Asset
curl https://api.adcrunch.dev/assets/ast_7c1e9a \
-H "Authorization: Bearer $ADCRUNCH_API_KEY"The first read after the PUT inspects the stored file and completes the Asset. The answer is the Asset, with the status ready or failed. A read before the PUT answers pending. Read again until the status is ready or failed.
AdCrunch also completes a reservation that nobody reads. The daily sweep inspects each pending Asset that is one day old. It completes the Asset when the file exists, and it deletes the reservation when no file exists.
Upload a Document
A Document has three steps, on the Brand:
POST /context/brands/{slug}/documentswithfilenameandcontent_type. The answer holdsdocumentId,uploadUrl, andexpiresInSeconds.PUTthe bytes touploadUrl, with theContent-Typeof the file.POST /context/brands/{slug}/documents/{documentId}/finalize.
The two flows use different names for some fields:
| Asset | Document | |
|---|---|---|
| Permission | asset:write |
brand:write |
| The id in the answer | assetId |
documentId |
| The end of the URL | expiresAt, a timestamp |
expiresInSeconds, a duration |
Header on the PUT |
If-None-Match: * |
Content-Type of the file |
| Read of the Asset before the bytes arrive | pending |
not applicable |
| Finalize before the bytes arrive | not applicable | 409 no_object |
The stored file is the only check
The storage URL applies no rule to the content of a file. It accepts each file that you send to it. So AdCrunch checks the file when it completes the Asset or the Document, from what storage holds: the size of the stored file, and, for a Document, the Content-Type that your PUT sent. What you declared at step 1 controls nothing.
For an Asset, AdCrunch also reads the header of the stored file. The bytes decide the type, and AdCrunch stores that type as mimeType. A PNG that your PUT sent as image/jpeg becomes a ready Asset of type image/png. The header gives width and height as the file is displayed, with its rotation applied, and durationMs for a video.
| Types | Largest file | |
|---|---|---|
| An image Asset | GIF, JPEG, PNG, WebP | 30 MB |
| A video Asset | MP4, QuickTime | 4 GB |
| A Document | PDF, GIF, JPEG, PNG, WebP | 25 MB |
A file that fails the check is deleted. A Document finalize answers 413 too_large or 415 unsupported_type. For an Asset, the read answers the Asset with the status failed and a failureCode. The Asset stays. See The status of an Asset. For an Asset, unsupported_type means that the bytes are no accepted type. An Asset can also have the failureCode unreadable, when the file ends before its header does. Reserve and upload again. When AdCrunch does not accept the declared content_type of a Document, step 1 also answers 415. That answer names the accepted types in allowed.
The status of an Asset
GET /assets/{id} answers an Asset in every status. GET /assets lists ready Assets only.
status |
Meaning |
|---|---|
pending |
AdCrunch has not inspected the bytes yet. |
ready |
AdCrunch inspected and accepted the bytes. You can register the Asset. |
failed |
AdCrunch refused the file and deleted it. failureCode holds the code and failureReason holds a sentence. Branch on failureCode, never on the sentence. |
failureCode is unsupported_type, too_large or unreadable. An import adds source_unreachable and source_length_unknown. It is null unless the status is failed. A failed Asset cannot recover. Create a new Asset.
Register an Asset
A Registration puts an Asset in the library of one advertiser, at its provider. The creative that you create with the Mutations API takes the Asset from that library.
curl -X POST https://api.adcrunch.dev/assets/ast_7c1e9a/registrations \
-H "Authorization: Bearer $ADCRUNCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "advertiserId": "acc_1203456789012345" }'
The answer is the Registration, with the status running. The provider does the work after the answer. To follow it, read Get one Asset: registrations holds each Registration of the Asset. Register an Asset to an advertiser shows each field of a Registration.
status |
Meaning |
|---|---|
running |
The provider has not finished. A video can stay running for several minutes, and that is not a failure. |
ready |
The file is in the library of the advertiser. providerIdentifier holds the id that the provider gave. |
failed |
The Registration did not occur. failureReason holds the reason. |
When a provider does not accept a Registration, the Registration ends failed, and failureReason says why. The Asset stays stored. What each provider supports lists the providers that accept a Registration.
When AdCrunch refuses the advertiser before any provider call, failureCode holds one of three codes. Branch on failureCode, never on the sentence in failureReason:
failureCode |
Meaning | What to do |
|---|---|---|
not_found |
Your organization owns no advertiser of this provider with this id. | Check the advertiser id. |
provider_not_connected |
The advertiser has no usable connection to the provider. | Reconnect the provider, then send the request again. |
missing_write_access |
The connection does not permit this kind of file. | Reconnect the provider to grant the permission, then send the request again. |
failureCode is null when the provider refused the file.
Only a ready Asset can be registered. A request for another Asset answers 409:
error |
Meaning | What to do |
|---|---|---|
asset_not_ready |
The Asset is pending. |
Read the Asset until its status is ready, then send the request again. |
asset_failed |
The Asset is failed. |
Read failureCode and failureReason, then create a new Asset. |
An Asset can go in the library of many advertisers, with one Registration for each. For one Asset and one advertiser:
- A Registration that is
failedcan start again. Send the same request. AdCrunch replaces the failed Registration with a new one. - A Registration that is
runningorreadyblocks a second one. The second request answers409already_registered.
Delete an Asset
DELETE /assets/{id} deletes the Asset from AdCrunch. It does not remove the file from the library of an advertiser, because that file belongs to the advertiser now. The answer counts those copies in remainingRegistrations.