Skip to content
Documentation
pozyskajpacjentaDocs

API referenceMedia

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

StatusWhen
400No file field
400The body is not multipart/form-data
401No Authorization header
401Unknown or rotated key
413The file is larger than 10 MB
415Not a JPEG, PNG, WebP or AVIF file
429Over 120 requests in this minute

Branch on the status and code, never on the Polish error text. All statuses in Errors.

400 · No file field
{
  "error": "multipart pole 'file' jest wymagane"
}
400 · The body is not multipart/form-data
{
  "error": "oczekiwano multipart/form-data"
}
401 · No Authorization header
{
  "error": "missing Authorization: Bearer <klucz z panelu Ustawienia>"
}
401 · Unknown or rotated key
{
  "error": "invalid key"
}
413 · The file is larger than 10 MB
{
  "error": "plik przekracza 10 MB"
}
415 · Not a JPEG, PNG, WebP or AVIF file
{
  "error": "niedozwolony typ image/svg+xml — dozwolone: image/jpeg, image/png, image/webp, image/avif"
}
429 · Over 120 requests in this minute
{
  "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-Remaining and RateLimit-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.