Operations
Each job runs an ordered list of operations against its
source. An operation has a type and a set of options.
The SDK's file-first chain exposes seven verbs:
| Operation | Does |
|---|---|
compress | Reduce file size for images, video, audio, and documents. |
convert | Change a file's format. |
thumbnail | Generate a preview image. |
merge | Combine several inputs into one output. |
archive | Bundle inputs into a ZIP or tarball. |
watermark | Stamp an image overlay or a text label onto an image. |
split | Fan one input into several outputs: PDF pages and GIF frames (stable), audio and video segments (beta). |
Images also have output(), a single image-transform
verb that keeps or changes the format and lowers to compress or convert for
you.
OperationTypeThese seven are the SDK's vocabulary, not the wire's. A verb lowers to one or
more contract operation types, and the contract's list is longer — it includes
image_watermark, text_watermark, video_watermark, video_text_watermark,
audio_to_video and passthrough, each with its own availability.
The mapping is not always one-to-one: watermark() routes to image_watermark
or video_watermark depending on the base file's media type, and a chained call
can lower to several jobs rather than several operations in one job.
You do not normally need the wire names — but if you are reading getSchema()
(see reading the schema at runtime), an error
payload, or the low-level operation(type, …) escape hatch, that is the
vocabulary you will meet, and it will not match this table.
Do not generalise from the first operation page you read. Each operation
declares its own accepted MIME types, and they genuinely differ — merge
accepts five audio types while compress and convert accept six, because
merge does not take audio/mp4.
The per-operation lists on each page are correct for that page only.
getSchema() returns the authoritative set at runtime, per operation and per
media type, and is the thing to trust if you are deciding programmatically —
read it like this, not by guessing its shape.
Options are media-type specific
The valid option keys for an operation depend on the input's media type: a
JPEG compress accepts different options than a video compress. The file-first
chain knows each input's media type and validates the options before any
upload — so a typo or an option that doesn't apply to the media type is caught
locally rather than coming back as a server 422.
- TypeScript
- PHP
- Python
- Rust
// Validated locally before the file is uploaded.
const result = await client
.compress('./photo.jpg', { quality: 80 })
.run({ maxWait: '5m' });
// Validated locally before the file is uploaded.
$result = $client
->compress('./photo.jpg', ['quality' => 80])
->run(new RunOptions(maxWait: '5m'));
Not available: the Python SDK is not published and no release is scheduled — see Python.
Not an application SDK: Rust is internal contract types — see Rust.
The authoritative option set
The contracts are the source of truth for which options each media type
accepts. To fetch the authoritative, per-MIME option list at runtime — for
building a dynamic UI, or to confirm what the API currently supports — call
getSchema() for the input's media type and operation.
Reading the schema at runtime
If you only want to know whether an operation is available, call
capabilities() first. It exists so that you do not have to handle
getSchema()'s result shape: it fetches the schema and returns the capability
matrix directly — a snapshot of every operation with no argument, or one
operation's entry when you pass its type. It is on the client from
gisl.create() / Gisl::create().
Reach for getSchema() when you need more than availability — the per-media-type
option sets, for example. Its result is not the schema itself. It supports
revalidation — pass a previous result's etag as ifNoneMatch — so it is
either a hit carrying the schema or a not-modified answer carrying none:
- TypeScript:
{ notModified: false, data, etag?, lastModified? }or{ notModified: true, etag?, lastModified? }. The schema is underdata. - PHP: a
GetSchemaHitResult(the schema is$result->schema) or aGetSchemaNotModifiedResult. Narrow withinstanceof.
The etag is the only validator the server honours. Both SDKs also accept an
ifModifiedSince option, but the server ignores If-Modified-Since today, so it
never produces a not-modified answer; lastModified is informational only.
In plain JavaScript, schema.operations is undefined, not an error — the
operations are at schema.data.operations. A caller that read the wrong path
and fell back with ?? 'stable' treated every operation as callable, so the
guard meant to skip an unavailable one never fired, and its tests stayed green.
Never default a missing entry to available. If your code needs a specific
operation and the schema has no entry for it, refuse — as the example does —
rather than falling back. Refuse a not-modified answer you did not ask for, too.
An empty operations map is a valid response (a mimeType or operation
filter can legitimately match nothing), so do not treat it as an error in
itself; the missing-entry check is what catches it when it matters to you. An
absent availability on an entry that is present means stable — the
contract says so — but only default once you have proved the entry is there.
- TypeScript
- PHP
- Python
- Rust
import { gisl } from '@giveitsmaller/sdk';
const client = await gisl.create({ apiKey: 'REPLACE_ME_API_KEY' });
// Availability only: no envelope to unwrap.
const splitCapability = await client.capabilities('split');
if (splitCapability === undefined) {
throw new Error('capabilities(): no entry for split');
}
console.log(splitCapability.availability ?? 'stable');
// The full schema: narrow on notModified, then hop through .data.
const schema = await client.getSchema();
if (schema.notModified) {
// No ifNoneMatch was sent, so there is no cached copy to reuse.
throw new Error('getSchema(): unexpected not-modified');
}
const operations = schema.data.operations;
const split = operations['split'];
if (split === undefined) {
throw new Error('getSchema(): no entry for split');
}
console.log(split.availability ?? 'stable');
use Gisl\Sdk\Gisl;
use Gisl\Sdk\GetSchemaHitResult;
$client = Gisl::create(apiKey: 'REPLACE_ME_API_KEY');
// Availability only: no envelope to unwrap.
$splitCapability = $client->capabilities('split');
if ($splitCapability === null) {
throw new \RuntimeException('capabilities(): no entry for split');
}
echo $splitCapability->getAvailability() ?? 'stable', "\n";
// The full schema: narrow to the hit, then read ->schema.
$result = $client->getSchema();
if (!$result instanceof GetSchemaHitResult) {
// A GetSchemaNotModifiedResult: no conditional header was sent, so there is no cached copy to reuse.
throw new \RuntimeException('getSchema(): unexpected not-modified');
}
$operations = $result->schema->getOperations();
$split = $operations['split'] ?? null;
if ($split === null) {
throw new \RuntimeException('getSchema(): no entry for split');
}
echo $split->getAvailability() ?? 'stable', "\n";
Not available: the Python SDK is not published and no release is scheduled — see Python.
Not an application SDK: Rust is internal contract types — see Rust.
capabilities() refuses nothing for you. It returns an empty snapshot (or
undefined / null for one operation) when the server omits the capability
matrix, or when a conditional header configured on the client turns the fetch
into a not-modified answer. A missing entry there means "not known", never
"available", so check it as the example does.
See the client reference for both methods' options.
Per-operation pages
Planned: transform
transform() — rotate by 0, 90, 180 or 270 degrees and flip none,
horizontal, vertical or both — exists in both SDKs but is not yet
available: every media type is planned, so workflow creation returns
feature_not_available (422).