Upload an image
POST/api/v1/media
- Since
- 1.2.0
- Auth
- Bearer key
- operationId
- uploadMedia
Uploads an image to the clinic's media library as multipart/form-data. The field file is required: JPEG, PNG, WebP or AVIF, at most 10 MB. SVG answers 415 (an SVG file can carry a script; SVGs uploaded earlier are still served, with a sandboxing CSP header). The file is served at the returned url; width and height are read from the file.
Request
Send the clinic's key in Authorization: Bearer (see API keys).
Body multipart/form-data
multipart/form-data with the file and its optional texts.
- filefilerequired
The image: JPEG, PNG, WebP or AVIF, at most 10 MB.
- altstring
Alternative text, up to 300 characters.
- captionstringsince 1.4.0
Caption, up to 300 characters.
- aiGeneratedstringsince 1.4.0
1 or true when the image was generated by AI.
One of
1true0false
curl -X POST "https://app.pozyskajpacjenta.pl/api/v1/media" \
-H "Authorization: Bearer $PP_API_KEY" \
-F "file=@gabinet-zabiegowy.webp" \
-F "alt=Gabinet zabiegowy z fotelem stomatologicznym" \
-F "caption=Gabinet zabiegowy, parter"import { readFile } from "node:fs/promises";
import { PozyskajPacjentaClient } from "@pozyskajpacjenta/sdk";
const client = new PozyskajPacjentaClient({
apiKey: process.env.PP_API_KEY ?? "",
baseUrl: "https://app.pozyskajpacjenta.pl",
});
const bytes = await readFile("gabinet-zabiegowy.webp");
const file = new Blob([new Uint8Array(bytes)], { type: "image/webp" });
const item = await client.uploadMedia(file, {
filename: "gabinet-zabiegowy.webp",
alt: "Gabinet zabiegowy z fotelem stomatologicznym",
caption: "Gabinet zabiegowy, parter",
});<?php
// wp_remote_post has no multipart helper: build the body with a boundary
$file = 'gabinet-zabiegowy.webp';
$boundary = wp_generate_password( 24, false );
$fields = array(
'alt' => 'Gabinet zabiegowy z fotelem stomatologicznym',
'caption' => 'Gabinet zabiegowy, parter',
);
$body = '';
foreach ( $fields as $name => $value ) {
$body .= "--$boundary\r\nContent-Disposition: form-data; name=\"$name\"\r\n\r\n$value\r\n";
}
$body .= "--$boundary\r\nContent-Disposition: form-data; name=\"file\"; filename=\"" . basename( $file ) . "\"\r\n"
. "Content-Type: image/webp\r\n\r\n" . file_get_contents( $file ) . "\r\n--$boundary--\r\n";
$response = wp_remote_post(
'https://app.pozyskajpacjenta.pl/api/v1/media',
array(
'headers' => array(
'Authorization' => 'Bearer ' . getenv( 'PP_API_KEY' ),
'Content-Type' => "multipart/form-data; boundary=$boundary",
),
'body' => $body,
'timeout' => 30,
)
);
if ( is_wp_error( $response ) ) {
error_log( $response->get_error_message() );
return;
}
$status = wp_remote_retrieve_response_code( $response );
$data = json_decode( wp_remote_retrieve_body( $response ), true );Response
201The file was stored.
- idstringrequired
Media id.
- r2Keystringrequireddeprecated
Internal storage key. Deprecated: do not use it or build addresses from it; use
url. - urlstringrequired
Path of the file, e.g. /api/media-file/…
- filenamestringrequired
Original file name.
- contentTypestring
MIME type, e.g. image/webp. Listed by GET /media.
- sizeinteger
Size in bytes. Listed by GET /media.
- altstring | null
Alternative text; null when empty.
- widthinteger | nullsince 1.4.0
Width in px; null when it could not be read from the file.
- heightinteger | nullsince 1.4.0
Height in px; null when it could not be read.
- aiGeneratedbooleansince 1.4.0
The image was generated by AI.
- captionstring | nullsince 1.4.0
Caption; null when empty.
- createdAtstring (date-time)since 1.9.1
Upload time, ISO 8601. Sent by GET /media (since 1.2.0), not by POST /media.
File stored
{
"id": "G4DBcqheiu_BMwt3Y_c_L",
"r2Key": "KdYaXquTXB/Hp7X9uzN-gabinet-zabiegowy.webp",
"url": "/api/media-file/KdYaXquTXB/Hp7X9uzN-gabinet-zabiegowy.webp",
"filename": "gabinet-zabiegowy.webp",
"alt": "Gabinet zabiegowy z fotelem stomatologicznym",
"width": 960,
"height": 600,
"aiGenerated": false,
"caption": "Gabinet zabiegowy, parter"
}Errors
| Status | When |
|---|---|
| 400 | No file field |
| 400 | The body is not multipart/form-data |
| 401 | No Authorization header |
| 401 | Unknown or rotated key |
| 413 | The file is larger than 10 MB |
| 415 | Not a JPEG, PNG, WebP or AVIF file |
| 429 | Over 120 requests in this minute |
Branch on the status and code, never on the Polish error text. All statuses in Errors.
{
"error": "multipart pole 'file' jest wymagane"
}{
"error": "oczekiwano multipart/form-data"
}{
"error": "missing Authorization: Bearer <klucz z panelu Ustawienia>"
}{
"error": "invalid key"
}{
"error": "plik przekracza 10 MB"
}{
"error": "niedozwolony typ image/svg+xml — dozwolone: image/jpeg, image/png, image/webp, image/avif"
}{
"error": "przekroczono limit 120 zapytań/min"
}Rate limit
- 120 requests per minute per key, shared by every endpoint.
- Every response to a valid key carries
RateLimit-RemainingandRateLimit-Reset; see Rate limits.
SDK method
client.uploadMedia(file: Blob, options?: { alt?, caption?, aiGenerated?, filename? }): Promise<MediaItem>
Throws ApiError 400, 413 or 415.
The TypeScript tab above uses it. Install and errors: TypeScript SDK.