Skip to main content
Every route in the API reference is a method on one of the facade’s accessors: pages(), blogPosts(), faqItems(), fetchskiMedia() and so on. A call is one line.
Attract::blogPosts() reaches the site FETCHSKI_ATTRACT_SITE names.
Print every method of an accessor with its route:
Terminal
Try one on the real site:
Terminal

Arguments

Named and typed

listBlogs(perPage: 20, search: 'launch'). Your editor completes them.

A body is arguments too

updateTestimonial(id: $id, quote: 'Great service', rating: 5): no model to build.

null is not sent

A PATCH changes only what you name, so nothing else is touched.

Laravel's own values

Carbon or a string for a date, a PHP enum or its value, $request->file() for a file.

Answers

Answers are readonly objects with properties, never getters.

What each type becomes

A field the site always sends is non-nullable. Lists are Collections. Date-times and Unix times are CarbonImmutable. Nested objects are their own classes, under Fetchski\AttractCmsLaravel\Data.
A known value is a PHP enum case; a value a newer site adds stays its string, so an older SDK never breaks on it.
Some models are both an answer and a body. A field the site always sends (a testimonial’s slug, visibility, archived) is non-null when it answers, yet stays an optional argument when you write: updateTestimonial(id: $id, quote: '…') leaves the others alone.
Against its type, before your code sees it. A value of the wrong type is an error naming the field, never coerced.

Whole lists, missing documents, jobs

  • …All() returns a LazyCollection that fetches a page at a time as you iterate: see Paging.
  • find…() sits beside each get…(): only a 404 becomes null.
  • A job’s wait() needs the job route, which only the management key reaches: on an API key it throws SiteConfigurationError naming the job id.

What an API key reaches

Every content type, with that type’s scopes: content:<type>:read lists and reads the published documents, :write creates and changes drafts (and reads them), :publish takes them live, and :delete deletes. Pages, the media libraries and Site settings (global:*) included. Each method’s reference page names its scope under Access; Required scopes lists them all.

Ids

A document’s id names its locale and mode: <document id>:<locale>:<mode>.

A create answers the id of its mode

Published by default (…:en:published); aposMode: 'draft' makes only a draft (…:en:draft).

Reads take either id

Or the bare document id.

Deleting takes the draft id

It removes both copies. The draft id is the published id with the last part changed.
An empty or blank id is refused before anything is sent, as a RequestNotSent: it would otherwise call another route (an empty getBlog id would read the whole list).

Uploads

Pass the file the request brought, as it is. It goes under the name it was uploaded with, as the site reads a file’s type from its extension:
uploadAttachment() needs the write scope of a media type (content:images:write, content:files:write or content:videos:write).
An upload is signed like any write: a key with a signing secret signs it by its SHA-256, which means reading the file twice (hashed, then sent). An UploadedFile, a path or an SplFileObject all can; a one-pass stream is sent unsigned, or refused when the key’s api_signing mode signs writes.
A signed upload signs its SHA-256. A multipart body isn’t signed byte for byte: the request declares the hex SHA-256 of the exact body in X-Fetchski-Content-SHA256 and signs that digest in the body’s place. The site checks the signature before it reads the upload, then the bytes against the digest, and stores nothing that doesn’t match. The SDKs do this for you; Signing requests shows it by hand.

Timeouts

Slow routes set their own

Each FetchskiAI route, and the blog import with images, carries its own timeout: the site’s 600-second cap per AI call plus 15 seconds. You never pass one per call.

The larger one wins

The client’s timeout (15 seconds by default) covers every other route. Raise it, and every call gets at least that long.
.env

A route without a method

Every route the site documents has a method, but request() calls any route exactly as the methods do: the key, its signature, retries, the timeout and the same errors. It returns the decoded JSON.

Next: working with results

Ids, counts and the fields you want, in one line.