Skip to content

Authorise a run before writing anything

POST
/entitlements/runs/authorise
curl --request POST \
--url https://api.sloose.com/entitlements/runs/authorise \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "declaredRows": 1, "templateId": "example", "libraryVersions": "example", "plan": "example", "finishes": "example", "retryOf": "example", "goesOver": "example", "resume": "example", "seenRun": "example", "importId": "example", "importVersion": 1, "kind": "real", "connectorId": "example" }'

Checks the run against the plan and returns a run id to report progress against.

Everything a run can be refused for is checked here, once, rather than part-way through an import that has already written half its rows. A set bigger than the plan allows in one run is refused with LIMIT_ROWS_PER_RUN; an organisation that has used its import runs for the period is refused with LIMIT_RUNS; and a run of an import while the organisation already has as many imports running as its plan allows at once (runs_concurrent, dry runs aside) is refused with LIMIT_RUNS_CONCURRENT, which carries that number as runsConcurrent. A run counts against the period once, at the first row it sends to be written — a row the CRM then refuses gives back the row, never the run — so a dry run, or a run cancelled or lost before its first row, costs nothing. Rows per month are bookkeeping for browser runs, never a refusal.

An authorised run completes unless it writes past the rows allowed in a single import. These checks can only test the number the caller gives, so …/progress tests what is actually REPORTED and stops a run that goes past it — see that route for the threshold, which is the lower of this declaration and the plan’s rows_per_run at the time. That is the one thing that can end a run after this point, and it STOPS the run rather than refusing it — the difference being that a stop happens to a run already writing, so what it wrote stays written and counted. It usually takes reporting more rows than the run said it had; it can also happen to an honest run whose plan is downgraded while it is still going.

Media typeapplication/json
object
declaredRows

How many rows the run intends to write. Must be a non-negative number. For a retry or a resume it also decides whether the run is a FREE link: one that declares more than its chain has left (what the run at its top had left of the lower of its declaration and the plan’s rows per run, less what the chain admitted, plus the rows it sends again) is charged as an ordinary run (see retryOf in the answer). A free link’s rows are its chain’s by their rowKey: one that writes a row new to its chain past the rows its chain was counted for (the declaration of the run at its top) is counted as a run after all, once, at that row — or stopped at that row where the period has no run left — and its kind and retryOf stay as authorised. This is judged by key only while the chain’s keys are kept: a day after the chain last ran, or for a chain begun before keys were kept, a link’s rows are judged by the count it declared instead, as they were before, and no run is counted at a later row.

number | null
templateId

The template this run came from, when it came from a saved one. Ignored unless it names a template in this organisation.

string | null
libraryVersions

An object: the helper libraries this run holds, namespace → { version, library } — the publish the page running it loaded: its number (versionNumber in the resolved configuration; 0 for one Sloose ships, or one stored before versions were kept) and its library’s own id (libraryId), since a library removed and published again starts at v1 under a new id. {} for a run that holds none. A bare number for a namespace is taken too, as a hold that names no library, which is what an older client sends. The page keeps them until the run ends, whatever is published meanwhile, and a publish’s impact check says so. Never refused: a record is kept only whole, so anything else — not an object, a version that is not a whole number — is kept as no record, a run that may hold any library.

plan

With importId: what the run sets out to walk — { files: [{ flowId, range: { startRow, endRow } }], from? }, its files in the order it will meet them, each with the rows it runs (endRow null: to the file’s end; an endRow before the startRow is an empty range: that file runs no row, and is kept as that without dropping the other files’ ranges), and, for a run picking up part-way, where it begins (from: { flowId, row }, a file of the plan and a row within its range; kept only so, #1252). Kept with the run’s snapshot and answered by GET /orgs/{org}/imports/{id}/runs/{run} as plan, so a run picking up where this one stops walks the same rows whatever the page shows by then. Never refused: anything else — a flow the import does not hold, a flow named twice, a range that is not whole rows — is kept as no plan, which a reader takes as unknown.

finishes

With importId: whether each file the run reads FINISHES A ROW WITH ITS RECORD, as the recipe the run runs says (#2283) — [{ flowId, finishes }], each flow once (flowId null for a run of one file). A file does when a row of it makes exactly one call of its own to the destination (a module’s write, a key-only create, or an after-save patch, one per field, on any module it runs; searches and look-ups change nothing), so the last rows a run writes of it are never “may be unfinished”. Kept with the run’s snapshot of that file and answered by GET /orgs/{org}/imports/{id}/runs/{run} as finishes. Anything else is dropped whole, never a reason to refuse the run; a run authorised without it says nothing, and readers mark its last rows as they always did.

retryOf

The run whose failed rows this one retries. Its rows are metered (they are written now). The retry is FREE — no second run against the period’s count while its rows are its chain’s (below), and authorised even when no runs are left — only when the named run ended in a status the ledger continues (complete, error or cancelled), recorded failed rows, and has had no free retry spent already (a free retry is spent at its first ADMITTED row: one that ends having admitted none is given back, and its run can be retried free again), and this would be no more than the third free retry in a row below a run the period counted (one that sent a row, whatever the CRM answered it): a run’s one free retry is SPENT once, at its first admitted row, a free retry that still has failed rows is retried free next, and the fourth in a row is a run of its own, which heads a chain of its own once it has been counted. Any other retry is allowed too, and is authorised, and counts, as a new run, refused with LIMIT_RUNS when none are left (the answer’s retryOf says which). A retry, or a resume, that names a link given back unspent stands below the run that link named: free exactly where a link below THAT run is — its one free link untaken, within the chain’s three, a counted top — and never more, so a string of links that sent nothing buys no second free link. With importId, the run it names is also what this one STANDS ON — any run of the import that has ended, however it ended, and was no dry run, whatever it wrote and whether this retry is free or not, unless the history’s purge has begun to take it, or has taken it (409 RUN_RETIRED, the answer to a name that finds no run of this organisation too) — and what that run’s chain made is gathered for it before anything is counted, for GET /orgs/{org}/imports/{id}/runs/{run}/inherited: a retry writes none of it again. A free link’s rows are its chain’s by their rowKey: one that writes a row new to its chain past the rows its chain was counted for (the declaration of the run at its top) is counted as a run after all, once, at that row — or stopped at that row where the period has no run left — and its kind and retryOf stay as authorised. This is judged by key only while the chain’s keys are kept: a day after the chain last ran, or for a chain begun before keys were kept, a link’s rows are judged by the count it declared instead, as they were before, and no run is counted at a later row.

string | null
goesOver

With importId and retryOf: the rows this retry sets out to GO OVER (#2337) — [{ flowId, row }], at most 1,000, each row once (flowId null for a run of one file). They are the rows the run it retries, or a run beneath that, wrote last before it stopped short, which may be unfinished (Written, may be unfinished in that run’s results file) and which this retry walks; a row struck out on Source or held by the row filter since is not walked, and is not named. Kept on the retry’s inheritance. Once the retry COMPLETES having written and recorded nothing, exactly the rows it named are finished: the stopped run’s file says Written of them and this run’s Written before. A row it did not name stays marked, and a retry that names none settles none. Naming a row that is in no such window settles nothing. Refused 400 where it is not such a list, and with resume (a resume names the rows it takes there).

resume

With importId, for a run picking up where another stopped: what it carries from that run (#1867) — { run, take, twice, left, stopped }. run is the run it picks up, of this import, ended and no dry run; named with no rows at all, it still carries what that run’s chain left, and waits for it to settle. take lists the rows before where it picks up, { flowId, row, modules }, whose create that run sent and never heard back about and which this run walks again — modules the modules of each whose create nobody answered, by API name, one at least, kept so that a run picking up where this one stops knows what it still owes. twice lists those creates it sends again where nothing can be looked for first, { flowId, row, module } (a row of take, the module by its API name): such a record may exist twice, and is said to. left lists the rows of that kind it leaves. stopped lists the rows before where it picks up that this run takes again by name, { flowId, row }: the rows that run had begun and left when it stopped (not_reached in its records, as a spent day leaves them), the last rows it wrote where it ended without its page and which may not have finished, and the rows that FAILED in it, which this run retries (#2116). Nothing of them is asked about, and what that run made of them is gathered with the rest, so a record of a failed row is not made twice. At most 1,000 of each. What that run (and any run it stood on) made of the rows taken is gathered for this one before anything is counted, as a retry’s chain is, for GET /orgs/{org}/imports/{id}/runs/{run}/inherited: written by neither this run nor any retry or resume of it again. left is kept there too, so it is still said. Not with retryOf. Anything but that shape is refused (400), because a carry read in part would have a record the run before made made again; a run that names no ended run of this import is refused too, and one the history’s purge has begun to take, or has taken, is 409 RUN_RETIRED.

seenRun

With importId, for a run that writes and continues no other (no retryOf, no resume): the run of this import the page last saw — the newest that did anything, as the page shows it — or null where it has seen none. The run is refused (409 RUN_SUPERSEDED) when a run of the import made since, that was no dry run, counted a row or logged a record: the page is behind, and what it is about to write may already be written. An id that is not a run of this import, or one the history has taken, is read as null. Absent: not asked, as for a client from before this was said. Not asked of a dry run, which writes nothing, nor without importId.

string | null
importId

The import this run runs. Present: the run CLAIMS the import (design §5.4) — in one transaction, only at importVersion and only while no other run holds it — snapshots its recipe and sources, and holds it on a two-minute lease renewed by POST /orgs/{org}/imports/{id}/runs/{run}/heartbeat, and by a write under the run (or a report of the rows it settled) once the lease is 20 seconds old. Only the session or token that authorised it may then write under it, renew it or close it. Absent: a run with no import, authorised as before.

string | null
importVersion

With importId: the version of the import the browser loaded. Required with it.

integer | null
kind

With importId: dry holds the claim like a real run and is recorded, but is never counted and never refused for the period’s runs — it writes nothing.

string | null
Allowed values: real dry
connectorId

Without importId: the connection this run writes to, which a run always names (design §14.8) — absent is 400 CONNECTION_REQUIRED, listing the connections. With importId the run writes to the import’s own connection, and this is not read.

string | null
key
additional properties

Authorised. Allowance is reserved.

Media typeapplication/json
object
ok
required
boolean
runId
required

Report progress against this id.

string
retryOf

The run this one was charged as a FREE retry of: counted as no second run while the rows it writes are its chain’s. A free link’s rows are its chain’s by their rowKey: one that writes a row new to its chain past the rows its chain was counted for (the declaration of the run at its top) is counted as a run after all, once, at that row — or stopped at that row where the period has no run left — and its kind and retryOf stay as authorised. This is judged by key only while the chain’s keys are kept: a day after the chain last ran, or for a chain begun before keys were kept, a link’s rows are judged by the count it declared instead, as they were before, and no run is counted at a later row. A retry of a run the server marked stopped by the CRM’s API allowance for the day is free whatever its failed rows, every time and outside the three free retries in a row (dayLink), as its resume is. A retry that names a link handed back unspent (it ended with no row admitted) reads as a retry of the run that link named. Null when it is no free retry — retryOf named no run, or one that is not this organisation’s, is still going, or was stopped, refused or abandoned rather than ended, one with no failed rows, one whose free retry was spent already (a retry that admitted a row), one that is already the third free retry in a row (so this would be the fourth), a run that was never a link and sent no row (it costs nothing and is not counted), or a retry declaring more than its chain has left (the lower of its top’s declaration and the plan’s rows per run: a retry may send again every row its chain admitted, but no new ones past that) — and the run was authorised, and counts, as an ordinary one. What it is charged as is not what it stands on: with importId, a run of the import retried as a new run — one stopped or abandoned, or any other — is still inherited from (GET …/runs/{run}/inherited says which run).

Or the run a resume picked up for free (resume, #1659, #1969): a resume of a run the server ended because its page went away (abandoned), once and inside the same three free links a retry keeps, or of a run the server marked stopped by the CRM’s API allowance for the day (ending cancelled, or abandoned where its page went before it closed), every time and outside those three. Only while the import holds exactly the files that run read, each read and cleaned as it was, and the recipe it read; and only while the resume declares no more than the chain has left — what the run at its top had left of the lower of its declaration and the plan’s rows per run, less what the chain admitted, plus the rows it takes again by name. Any other resume is charged as an ordinary run, and this is null.

string | null
dayLink

The free link picks up a run the CRM’s spent day stopped, by a resume or a retry (#1969): it counts against none of the three free links in a row. Absent otherwise.

boolean
kind

With importId: what the run was recorded as, after the meter’s decision.

string
Allowed values: real dry retry
freeRetriesLeft

How many free links in a row — retries, and resumes of a run its page left — are left below this run: three for a run of its own (it heads a chain once the period counts it), and one fewer for each such free link it is; a resume of a run the CRM’s spent day stopped (dayLink) takes none. A retry of it is free only while this is above 0, and only if it ends as the client says, with failed rows; a resume of it, only while this is above 0 and if its page left it (a spent day’s resume is free whatever this says). Either only while it declares no more than its chain has left (declaredRows). Absent for a dry run, which the meter is not asked about.

integer
key
additional properties
Example
{
"ok": true,
"dayLink": true,
"kind": "real"
}

declaredRows is missing, negative or not a number; importId came without the importVersion it was loaded at; resume is not the shape it takes, comes with retryOf, or names no ended run of this import that is no dry run; or a run with no import named no connection (code: CONNECTION_REQUIRED, with connections).

Media typeapplication/json
object
error
required
string
key
additional properties
Examplegenerated
{
"error": "example"
}

No bearer, or one that no longer resolves: a session that is unknown or expired (code: AUTH_REQUIRED), or an API token that was revoked or expired (TOKEN_REVOKED, TOKEN_EXPIRED), whose organisation is gone (ORG_NOT_FOUND) or whose member or user row is (MEMBERSHIP_GONE).

Media typeapplication/json

The error envelope every non-2xx answer uses.

object
error
required

Human-readable explanation.

string
code

Machine-readable reason. Absent on a few legacy 400s.

string
key
additional properties
Examplegenerated
{
"error": "example",
"code": "example"
}

Refused. A plan or status refusal (ok: false, code a reason — the set is too big for one run, the period’s runs are used, as many imports are running as the plan allows at once, or the organisation is suspended); or, for a run of an import, an operator running somebody else’s import (code: NOT_YOURS) or a credential nothing could hold a run with (code: NO_RUN_HOLDER), each { error, code }. Or the connection’s system is known to be refusing calls because its daily API allowance is spent (code: DESTINATION_ALLOWANCE_SPENT, { error, code, spentAt }, where spentAt is when Sloose first heard): a run that writes is not started then — nothing is claimed, nothing is counted and no run is recorded — and error is a sentence to show as it is. A dry run is never refused for it, and nor is any run while the allowance cannot be asked about (the check gives up after two seconds and the run is authorised). Not one of the plan’s reasons: it says nothing of the organisation’s plan, and GET …/connectors/{id}/credits says when it clears. Like every route behind a session: a session signed in but in no organisation is refused code: NO_ORG, a banned person code: BANNED, and a closed organisation code: ORG_CLOSED.

Media typeapplication/json
Any of:
object
ok
required
boolean
code
required

Why an action was refused. A closed set: the widget switches on it.

string
Allowed values: LIMIT_ROWS LIMIT_RUNS LIMIT_RUNS_CONCURRENT LIMIT_ROWS_PER_RUN LIMIT_CONNECTORS LIMIT_SCHEDULES LIMIT_CRM_USERS LIMIT_FILES_BYTES LIMIT_FILE_SIZE FEATURE_SHARED_TEMPLATES FEATURE_HELPER_LIBRARIES FEATURE_SERVER_RUN FEATURE_MCP FEATURE_ROLLBACK FEATURE_SCHEDULED_IMPORTS CREDITS_EXHAUSTED STATUS_SUSPENDED STATUS_PAST_DUE
allowanceRemaining
number
runsRemaining
number | null
rowsPerRun

With LIMIT_ROWS_PER_RUN: the most rows one run may declare.

number
runsConcurrent

With LIMIT_RUNS_CONCURRENT: how many imports the plan lets run at once.

number
retryOf

When the meter refused it: what the run was asked as, by the rule an authorised run is charged by — the run it would have been a FREE retry of, or null for a run of its own, a retry charged as a new run included. Absent when the meter was not asked.

string | null
key
additional properties
Example
{
"ok": false,
"code": "LIMIT_ROWS"
}

No such import (code: IMPORT_NOT_FOUND), or the connection it names is not one this person — or token — may use (code: CONNECTION_NOT_FOUND).

Media typeapplication/json
object
error
required
string
code
required
string
key
additional properties
Examplegenerated
{
"error": "example",
"code": "example"
}

The import cannot be claimed: another run holds it (code: IMPORT_RUNNING), it changed since this version was loaded (code: IMPORT_CHANGED, with the version it is at now), or it names no connection yet (code: NO_CONNECTION). Or the connection the run would write through cannot write — disconnected, holding no credential, its grant refused by the CRM since, or granted before connecting asked for records (code: NOT_CONNECTED): refused before anything is claimed or counted. Or a retry’s file was read under options a newer build of Sloose wrote (code: FROM_FUTURE), which this build cannot compare with the run it retries: the claim is given back and nothing is counted. Or the run a retry names, or a resume picks up (resume.run), is past its plan’s history, removed or being removed (code: RUN_RETIRED, which a name that finds no run of this organisation is answered with too) — refused before the claim, or, when the purge begins while what that run made is read, after it, with the claim given back — since what it made can no longer be read in full; or the run a retry names or a resume picks up, or a run before it that it stands on, wrote to another connection than the import’s (code: CONNECTION_MISMATCH) — refused before the claim for the run named, and with the claim given back for one found further back as the chain is read — since what that chain made is not this connection’s, and the import is run again instead; or this deployment cannot reach the storage a retry’s or a resume’s inheritance is read from (code: NOT_CONFIGURED), with the claim given back. Or the run a resume picks up, or a retry stands on, has not settled — a write of it, or of a run it follows, Zoho has not answered or an answer not yet written down (code: RUN_SETTLING): wait and resume or retry again — or never can, its records given up on (code: RUN_RECORDS_LOST); both before anything is claimed, and both said of a run made after it too, where that run left no count of what it sent. Or the run a resume picks up, or a retry names, is no longer the import’s newest run that did anything (code: RUN_SUPERSEDED): a later run of the import that was no dry run counted a row or logged a record — another tab’s, or another person’s — so the rows this would walk may already be written. Refused before anything is claimed, and by the claim itself for a run that began and ended while this was asked; read the import’s runs again and continue from the newest. A run that continues none is refused the same way from the run its page last saw (seenRun), where it says one.

Media typeapplication/json
object
error
required
string
code
required
string
key
additional properties
Examplegenerated
{
"error": "example",
"code": "example"
}

The restore switch is on (code: MAINTENANCE): every route but /health and /robots.txt is refused before anything is read, except the operator’s own POST /staff/people/{id}/delete with the ADMIN_TOKEN. Sloose is down for maintenance. Please try again in a few minutes.

Media typeapplication/json
object
error
required
string
code
required
string
Allowed value: MAINTENANCE
Example
{
"code": "MAINTENANCE"
}
Retry-After
integer

Seconds until asking again is worth it (300 while the switch is on).

Report a problem with this page

sloose.com