Subir multimedia para plantilla
curl --request POST \
--url https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form file=@example-file \
--form type=image/jpegimport requests
url = "https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = { "type": "image/jpeg" }
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('type', 'image/jpeg');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nimage/jpeg\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nimage/jpeg\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nimage/jpeg\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nimage/jpeg\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"h": "4::aW1hZ2UvanBlZw==:ARZ...:e:1770000000:..."
}{
"error": "Authorization invalid"
}{
"error": "Content Too Large"
}{
"error": "Unsupported Media Type"
}{
"error": {
"status": 400,
"errors": [
{
"code": "377",
"name": "META_WHATSAPP_MEDIA_URL_NOT_ALLOWED",
"message": "Media URL not allowed"
}
]
}
}{
"error": "<string>"
}{
"error": "integration_timeout",
"message": "The operation took too long to complete. Try again in a few moments."
}Otros endpoints
Subir multimedia
Obtén el header_handle para plantillas con HEADER de imagen, vídeo o documento
POST
/
whatsapp
/
businesses
/
{wabaId}
/
templates
/
media
Subir multimedia para plantilla
curl --request POST \
--url https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form file=@example-file \
--form type=image/jpegimport requests
url = "https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media"
files = { "file": ("example-file", open("example-file", "rb")) }
payload = { "type": "image/jpeg" }
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, data=payload, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
form.append('type', 'image/jpeg');
const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
options.body = form;
fetch('https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nimage/jpeg\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: multipart/form-data"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nimage/jpeg\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media")
.header("Authorization", "Bearer <token>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nimage/jpeg\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"type\"\r\n\r\nimage/jpeg\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"h": "4::aW1hZ2UvanBlZw==:ARZ...:e:1770000000:..."
}{
"error": "Authorization invalid"
}{
"error": "Content Too Large"
}{
"error": "Unsupported Media Type"
}{
"error": {
"status": 400,
"errors": [
{
"code": "377",
"name": "META_WHATSAPP_MEDIA_URL_NOT_ALLOWED",
"message": "Media URL not allowed"
}
]
}
}{
"error": "<string>"
}{
"error": "integration_timeout",
"message": "The operation took too long to complete. Try again in a few moments."
}Conceptos
Meta no acepta el archivo directamente al crear la plantilla. Exige unheader_handle producido por la Resumable Upload API, que depende del token de la aplicación; por eso la subida pasa por aquí y no por tu frontend.
Este endpoint recibe el archivo, o su enlace público, y devuelve el handle listo para usar.
Cuándo lo necesitas
Solo cuando la plantilla tiene un componenteHEADER de tipo IMAGE, VIDEO o DOCUMENT. Las plantillas con header de texto, o sin header, no necesitan subida.
Dos formas de enviar
El formato se elige por elContent-Type de la petición.
- Archivo (multipart)
- Enlace público (JSON)
multipart/form-data con el archivo en el campo file.El campo type es opcional. El MIME type usado es el de la propia parte file; type solo se tiene en cuenta cuando la parte llega sin tipo o como application/octet-stream.curl -X POST https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media \
-H "Authorization: Bearer SU_SECRET_KEY" \
-F "file=@banner.jpg;type=image/jpeg"
application/json con la dirección pública del multimedia en el campo url.curl -X POST https://api.omni.z-api.io/whatsapp/businesses/{wabaId}/templates/media \
-H "Authorization: Bearer SU_SECRET_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://cdn.ejemplo.com/banner.jpg"}'
http o https y pública: las direcciones privadas o internas se rechazan.Flujo
1
Sube el archivo o el enlace
POST /whatsapp/businesses/{wabaId}/templates/media, de una de las dos formas anteriores.2
Guarda el handle
La respuesta trae el handle en el campo
h.3
Úsalo en el componente HEADER
Pasa el handle como
example del HEADER en Referencia de la API:{
"type": "HEADER",
"format": "IMAGE",
"example": { "header_handle": ["4::aW1hZ2UvanBlZw==:ARZ..."] }
}
El handle caduca. Sube el archivo y crea la plantilla en la misma sesión: no guardes el handle para usarlo días después.
En archivos más grandes, el handle puede llegar con saltos de línea. Usa el valor de
h exactamente como lo recibes, sin quitar nada.Requisito de configuración
La subida depende del App ID de Meta registrado en la cuenta. Sin él, la llamada devuelve422, con el código 375 en el cuerpo de error.
Esto se configura en el panel, en Seguridad: no hay endpoint público para registrar las credenciales del app. Consulta también Orígenes del SDK, que cubre el otro requisito del flujo con SDK.
Límites
| Elemento | Valor |
|---|---|
| Tamaño máximo por archivo | 100 MB |
| Tamaño máximo por enlace | 5 MB |
| Campo obligatorio (multipart) | file |
| Campo obligatorio (JSON) | url |
Errores
| Estado | Cuándo |
|---|---|
400 | Falta file o está vacío, falta url o está en blanco, JSON no válido, o secret key no válida ({"error": "Authorization invalid"}) |
413 | Archivo de más de 100 MB |
415 | Content-Type distinto de multipart/form-data y application/json |
422 | La plataforma o Meta rechazó la subida, o Meta falló al procesarla (formato, tamaño, token vencido o Meta no disponible). El cuerpo de error llega en error. Códigos más comunes: 375 (canal sin App ID), 377 (enlace no permitido), 380 (error al descargar el enlace) |
502 | Plataforma no disponible. Un rechazo o falla de Meta no llega como 502: llega como 422 |
Autorizaciones
Secret Key generada en el panel de Seguridad de Omni Z-API
Parámetros de ruta
ID de la WABA (obtenido con Listar WABAs)
Ejemplo:
"428083093730937"
Cuerpo
multipart/form-dataapplication/json
Respuesta
Subida completada
Handle que se usa como example del componente HEADER al crear la plantilla