Skip to content

Apply a configuration bundle

POST
/orgs/{org}/config/bundle
curl --request POST \
--url 'https://api.sloose.com/orgs/org_9f3c/config/bundle?connector=con_8f2a' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'If-Match: "7"' \
--data '{ "bundleVersion": 1, "org": { "dateFormat": "DMY", "expressionContext": "example", "aiSamples": "allow" }, "registry": { "version": 1, "modules": [ { "seedMappings": "example", "key": "example", "apiName": "example", "label": "example", "singular": "example", "singularLabel": "example", "color": "example", "defaultEnabled": true, "uniqueFields": [ "example" ], "defaultDuplicateCheck": { "searchField": "example", "searchMethod": "example", "expression": "example" }, "excludedFields": [ "example" ], "child": { "of": "example", "via": "example", "parentNeedsRows": true, "pointer": "example", "maxRows": "off", "layouts": { "additionalProperty": { "name": "example", "maxRows": "off", "required": [ "example" ] } }, "defaultLayout": "example" }, "stockEffect": { "field": "example", "label": "example", "direction": "up" } } ], "expressionContext": "example", "apiDelayMs": 1, "systemExcludedFields": [ "example" ], "dateFormat": "DMY", "timezone": "example" }, "fields": { "additionalProperty": [ { "apiName": "example", "description": "example", "format": "example", "examples": [ "example" ], "aliases": [ "example" ], "excludeFromAi": true, "derived": true, "defaultMapping": { "mode": "column", "source": "example", "staticValue": "example", "staticPickedIn": { "connection": "example", "module": "example", "id": "example", "name": "example" }, "transform": "example", "skip": true, "searchField": "example", "searchMethod": "example", "searchExpression": "example", "patch": { "when": [ "pending" ], "overwrite": "always" }, "files": { "sheet": "example", "holds": "name", "separator": "example" }, "key": true, "keyMethod": "example", "keyExpression": "example", "onNone": "empty", "onSeveral": "ask" }, "excluded": true, "requiredForImports": "waivable", "writtenAs": "upper", "mustBe": { "rule": "example", "says": "example" } } ] }, "libraries": [ { "manifest": { "namespace": "example", "version": "example", "scope": "global", "description": "example", "functions": [ { "name": "example", "description": "example", "params": [ { "name": "example", "type": "example", "optional": true, "description": "example" } ], "returns": "example", "examples": [ { "call": "example", "result": "example" } ], "tags": [ "example" ] } ] }, "source": "example" } ], "templates": [ { "name": "example", "description": "example", "config": { "additionalProperty": "example" } } ], "projects": [ { "name": "example", "description": "example", "config": { "additionalProperty": "example" } } ] }'

Applies a whole configuration repository in one call — registry, annotations, settings, helper libraries and seeded templates. This is what the SDK’s push sends.

The registry and annotations are ONE connection’s: the one ?connector= names, which a push must (400 CONNECTION_REQUIRED otherwise). The libraries, the organisation settings and the templates are the organisation’s, and a pushed template names no connection: it is picked for on Target when it is used.

A bundle’s templates belong to the whole organisation, so pushing them is sharing: a plan without shared templates is refused rather than silently importing them as private.

Its helper libraries are each compiled and loaded against their manifests before anything is written, and published as a new version only when one differs from what the organisation already has — so a push that sends the same libraries every time makes no versions and moves the configuration’s version no further for them. The registry and each module’s annotations are compared the same way (#1589): only a part that differs from what the connection holds is written, and only a part written moves the version — so a push of what is already there moves it not at all, and under If-Match answers the version it was sent. A library the push makes is the repository’s (managedBy: sdk), read only on its page; one taken over in the browser is left as it is, and warnings says so (libraries/<name>: taken over in the browser…). Publishing libraries is the helper_libraries plan feature.

org
required

The organisation id the session was issued for. A token for one organisation can never read another.

string
Example
org_9f3c

The organisation id the session was issued for. A token for one organisation can never read another.

connector

The connection this is about, by id: one of the organisation’s, or your own. REQUIRED: absent or empty is 400 CONNECTION_REQUIRED, listing the connections this bearer may name, because nothing picks one for a request that does not say (design §14.8). Any other — another organisation’s, or another person’s — is 404 CONNECTION_NOT_FOUND.

string
Example
con_8f2a

The connection this is about, by id: one of the organisation’s, or your own. REQUIRED: absent or empty is 400 CONNECTION_REQUIRED, listing the connections this bearer may name, because nothing picks one for a request that does not say (design §14.8). Any other — another organisation’s, or another person’s — is 404 CONNECTION_NOT_FOUND.

If-Match

The registryVersion this change was made against, as "7" or 7 — a weak tag (W/"7") is refused, since If-Match uses strong comparison. Sent, the write is refused with 409 (code: REGISTRY_CONFLICT, the current version in the body) when the organisation has moved since, and otherwise answers the version it made: one past the version sent for a write made in one transaction, or the version sent itself for a request that asks for no change (an empty PATCH /config/writing), since it makes none. A bundle (POST /config/bundle) is several transactions and only its first waits on this version: it answers the version its own bumps took the organisation to — each part bumps only when it writes something, so a push of what is already there answers the version sent — and none — nor an etag — when another write landed between them; one landing after them, while its templates are saved, withholds the etag alone. Omitted, there is no condition, and * is no condition either — and a write with no condition answers no registryVersion (nor, from a bundle, an etag), since nothing says which version it was made from. A value that is neither is 400 (code: BAD_IF_MATCH) rather than ignored — a precondition silently dropped looks like conflict detection and is not.

string
Example
"7"

The registryVersion this change was made against, as "7" or 7 — a weak tag (W/"7") is refused, since If-Match uses strong comparison. Sent, the write is refused with 409 (code: REGISTRY_CONFLICT, the current version in the body) when the organisation has moved since, and otherwise answers the version it made: one past the version sent for a write made in one transaction, or the version sent itself for a request that asks for no change (an empty PATCH /config/writing), since it makes none. A bundle (POST /config/bundle) is several transactions and only its first waits on this version: it answers the version its own bumps took the organisation to — each part bumps only when it writes something, so a push of what is already there answers the version sent — and none — nor an etag — when another write landed between them; one landing after them, while its templates are saved, withholds the etag alone. Omitted, there is no condition, and * is no condition either — and a write with no condition answers no registryVersion (nor, from a bundle, an etag), since nothing says which version it was made from. A value that is neither is 400 (code: BAD_IF_MATCH) rather than ignored — a precondition silently dropped looks like conflict detection and is not.

Media typeapplication/json
object
bundleVersion
required
number
Allowed values: 1
org
object
dateFormat
string
Allowed values: DMY MDY YMD
expressionContext
string
/^[A-Za-z_$][\w$]*$/
aiSamples
string
Allowed values: allow masked deny
registry
required
object
version
integer
modules
required
Array<object>
object
seedMappings

seedMappings is no longer a module setting: a mapping a new import starts from belongs to the field it is about. Set defaultMapping on the field annotation — PATCH /orgs/{org}/config/fields/{module}/{apiName}, or fields/<module>.json in a configuration repository. Values already stored were moved by migration 0035.

key
required
string
/^[a-z][a-z0-9_]*$/
apiName
required
string
>= 1 characters
label
required
string
>= 1 characters
singular
required
string
/^[A-Za-z_$][\w$]*$/
singularLabel

One record as the CRM names it in words (“Sales Order”), which sentences say; singular is the identifier expressions use (“SalesOrder”). Read-only: discovery takes it from the CRM, so a pushed value is ignored. Absent until a discovery has read it, when a reader says singular.

string
color
string
defaultEnabled
boolean
uniqueFields
Array<string>
defaultDuplicateCheck
object
searchField
string
searchMethod
string
expression
string
excludedFields
Array<string>
child
object
of
required
string
/^[a-z][a-z0-9_]*$/
via
required
string
>= 1 characters
parentNeedsRows
boolean
pointer
string
>= 1 characters
maxRows
Any of:
integer
> 0
layouts
object
key
additional properties
object
name
required
string
maxRows
Any of:
integer
> 0
required
required
Array<string>
defaultLayout
string
>= 1 characters
stockEffect
object
field
required
string
>= 1 characters
label
required
string
>= 1 characters
direction
required
string
Allowed values: up down
expressionContext
string
/^[A-Za-z_$][\w$]*$/
apiDelayMs
integer
<= 60000
systemExcludedFields
Array<string>
dateFormat
string
Allowed values: DMY MDY YMD
timezone

The organisation’s IANA timezone, which helpers.today() answers in. Read-only: it is taken from the CRM at sign-in, so a pushed value is ignored.

string
<= 64 characters
fields
object
key
additional properties
Array<object>
object
apiName
required
string
>= 1 characters
description
string
format
string
examples
Array<string>
aliases
Array<string>
excludeFromAi
boolean
derived
boolean
defaultMapping
object
mode
string
Allowed values: column static expression search skip
source
string | null
staticValue
string | null
staticPickedIn
object
connection
required
string
>= 1 characters <= 100 characters
module
required
string
>= 1 characters <= 200 characters
id
required
string
>= 1 characters <= 200 characters
name
required
string | null
<= 500 characters
transform
string | null
skip
boolean
searchField
string | null
searchMethod
string | null
searchExpression
string | null
patch
object
when
Array<string>
Allowed values: pending created updated exists skipped filtered guarded dryrun error blocked
overwrite
string
Allowed values: always if-absent
files
object
sheet
string | null
>= 1 characters
holds
required
string
Allowed values: name name-no-extension path path-end pattern row
separator
string | null
>= 1 characters <= 5 characters
key
boolean
keyMethod
string | null
keyExpression
string | null
onNone
string | null
Allowed values: empty skip ask
onSeveral
string | null
Allowed values: ask first skip empty
excluded
boolean
requiredForImports
Any of:
boolean
writtenAs
string
Allowed values: upper lower title
mustBe
object
rule
required
string
>= 1 characters <= 500 characters
says
required
string
>= 1 characters <= 200 characters
libraries
Array<object>
object
manifest
required
object
namespace
required
string
/^[a-z][a-z0-9_]*$/
version
required
string
/^\d+\.\d+\.\d+$/
scope
required
string
Allowed values: global org
description
string
functions
required
Array<object>
object
name
required
string
/^[A-Za-z_$][\w$]*$/
description
required
string
params
required
Array<object>
object
name
required
string
type
required
string
optional
boolean
description
string
returns
required
string
examples
Array<object>
object
call
required
string
result
string
tags
Array<string>
source
required
string
templates
Array<object>
object
name
required
string
>= 1 characters <= 200 characters
description
string
<= 2000 characters
config
required
object
key
additional properties
projects

Deprecated: the older name of templates, read as templates. A bundle may send one or the other, not both.

Array<object>
object
name
required
string
>= 1 characters <= 200 characters
description
string
<= 2000 characters
config
required
object
key
additional properties

Applied: what changed and, for a push sent with a version in If-Match, the registry version it produced with the resolved-config etag for that same state. Another write landing between the push’s own version bumps withholds both; one landing after the last of them — while the templates are saved, before the state is hashed — withholds only the etag. A push without If-Match answers neither.

Media typeapplication/json
object
ok
required
boolean
etag

The resolved configuration’s ETag for the state this push produced — only beside registryVersion, and only when no other write landed before it was taken. It is taken last, after the push’s templates are saved, so another write landing after the push’s last version bump — while the templates are saved, say — withholds this alone: registryVersion is still answered, since it names only the version you sent and this push’s own changes, and this would name the other write too.

string
registryVersion

The version this push’s own writes took the organisation to, to send as the next write’s If-Match. Only a push sent with a version in If-Match answers one, and only when no other write landed between the push’s own version bumps — the registry, each module’s annotations, the libraries, each of which bumps only when it writes something (#1589), so a push of what is already there answers the version it was sent: a bundle is several transactions, of which only the first can be made on the version sent, and a version with somebody else’s change inside it would label a change you were never shown. A write landing after the last of them — while the templates, which move no version, are saved — is not inside this number: it is still answered, with etag withheld, and a write sent under it is refused (409), since the organisation has moved past it. A push without one (no header, or *) answers neither this nor etag, since nothing says which version your picture was built from — read the configuration for a version to write against.

number
modules
required
number
annotations
required
number
libraries
required
number
templates
required

created: templates the push made. updated: existing templates it changed — their name, description, kind or recipe, or a connection it cleared (a pushed template names none) — and, conservatively, one whose write kept losing to other pushes and was made without its precondition, which is counted whether or not it differed. A template re-sent unchanged is neither.

object
created
required
number
updated
required
number
warnings

What the push stored or left alone and has something to say about, one sentence each, starting with the part’s path in the bundle — present only when there is something. A library taken over in the browser is left as it is (libraries/<name>: taken over in the browser…), first. And a template enabling a module the connection has but does not offer once this push’s registry is applied — one the registry leaves out, or one gone from the CRM: the template is stored, and an import started from it parks that module’s setup until the module is offered again. A module the connection has never had is still refused (422 UNKNOWN_MODULES).

Array<string>
key
additional properties
Example
{
"ok": true
}

The request — its body or its query — did not match the schema (issues carries the Zod issue list), or: The request named no connection (code: CONNECTION_REQUIRED, with connections: the ones this bearer may name). Nothing picks one for a request that does not say (design §14.8).

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"
}

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"
}

The plan cannot share templates (code: FEATURE_SHARED_TEMPLATES) or publish helper libraries (code: FEATURE_HELPER_LIBRARIES, for a bundle carrying any), the role is too low, or the organisation is closed (code: ORG_CLOSED). An organisation closed during the push is found when a new template is written, after the registry, the annotations, the libraries and any earlier templates committed: written says which. 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
object
error
required
string
code
required
string
written

ORG_CLOSED only, when the organisation closed among the push’s templates: what had committed before it, which the organisation’s change log records. Absent when it was closed before the push began, and on every other refusal.

object
registry
required
boolean
annotations
required

The modules whose annotations landed before the push stopped.

Array<string>
libraries
required

Whether the bundle’s libraries were applied.

boolean
templates
required

The templates saved before the push stopped, by name.

Array<string>
key
additional properties
Examplegenerated
{
"error": "example",
"code": "example",
"written": {
"registry": true,
"annotations": [
"example"
],
"libraries": true,
"templates": [
"example"
]
}
}

?connector= names a connection this person may not use: another organisation’s, or another person’s (code: CONNECTION_NOT_FOUND).

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"
}

Or a bound on what an organisation’s libraries hold is met, the same on every plan (holding says where it stands with the caps, proposed what this would have brought it to): LIBRARY_LIMIT — it would hold more than 20 libraries; LIBRARIES_TOO_LARGE — its libraries would come to more than 1 MB of source; LIBRARY_HISTORY_FULL — the versions it keeps would come to more than 25 MB; LIBRARY_PUBLISH_RATE — more than 50 versions in 24 hours, counted from the first of them. A publish that changes nothing makes no version and meets none of them. Checked before the push writes anything, where it is refused whole with nothing written and no written in the body. Where the bound is met only while the push is applied (a publish from a library’s page landed in between), its libraries are refused together in their own transaction and written says what had been written by then: the registry and the annotations it names, which stay. The organisation’s configuration has changed since the version in If-Match (code: REGISTRY_CONFLICT, the current version in the body), and nothing was written. Or another change overtook the push part way (code: PUSH_OVERTAKEN): a module’s fields changed while it was applied, typically a removed module taking its field rows, or discovery changing a field’s kind to one that takes no case the push gave it. The push stopped there. written says what had committed, the organisation’s change log records it, and no version is answered, since the push was not alone. Push again: what landed is written again to the same effect, and a field gone for good is then refused up front, by name.

Media typeapplication/json
Any of:
object
error
required
string
code
required
string
Allowed values: REGISTRY_CONFLICT
registryVersion
required
number
key
additional properties
Example
{
"code": "REGISTRY_CONFLICT"
}

The bundle gives a field a case its kind does not take (code: WRITTEN_AS_NOT_FOR_KIND, each named in fields) or a Must be rule that reaches past the value (code: INVALID_RULE, each named in fields with why), has a template enabling a subform (code: MODULE_IS_SUBFORM, naming the template and each subform), names a module or field this organisation does not have (code: UNKNOWN_MODULES, UNKNOWN_FIELDS; a field the CRM no longer returns is one it has, kept on purpose, and its annotation is written), gives a default match rule or a template’s key a field the destination cannot match by (code: UNMATCHABLE_FIELD, each named in rules), or carries a helper library that cannot be published: global in scope (GLOBAL_LIBRARY), sent twice (DUPLICATE_LIBRARY), named core or after one of Sloose’s own helpers (RESERVED_NAMESPACE), or one that does not compile or load against its manifest (LIBRARY_DOES_NOT_LOAD, saying which and why) — each checked before the push writes anything, so nothing of it lands. A template enabling a module the connection has, but that this push leaves unoffered, is not refused: it is stored, and named in warnings. A push another write overtook — one that moved a module a template names — is checked again against the modules as its writes left them, and refused for a name none holds any longer: its registry, annotations and libraries are then applied, no template is (written says which parts committed, and the organisation’s change log records them), and a push made again completes.

Media typeapplication/json
object
error
required
string
code
required
string
template

UNKNOWN_MODULES only: the template that names a module none holds.

string
missing

UNKNOWN_MODULES only: the modules no one holds.

Array<string>
subforms

MODULE_IS_SUBFORM only: each subform the template enables, named in template.

Array<string>
written

UNKNOWN_MODULES only, refused after the push’s writes: what had committed before it. Absent for a refusal before any write.

object
registry
required
boolean
annotations
required

The modules whose annotations landed before the push stopped.

Array<string>
libraries
required

Whether the bundle’s libraries were applied.

boolean
templates
required

The templates saved before the push stopped, by name.

Array<string>
fields

WRITTEN_AS_NOT_FOR_KIND: each field given a case its kind does not take. INVALID_RULE: each field given a Must be rule that reaches past the value, and why.

Array
Any of:
object
module
required
string
apiName
required
string
kind
required

The field’s kind, as discovery last read it.

string
writtenAs
required
string
Allowed values: upper lower title
takes
required

The cases this kind takes: none for a website, lower for an email.

Array<string>
Allowed values: upper lower title
rules

UNMATCHABLE_FIELD only: each default match rule or template key that looks in a field the destination does not take with the method it asks, and why.

Array<object>
object
module
required
string
field
required
string
words
required

In words: the field cannot be matched with this method, and why.

string
key
additional properties
Example
{
"fields": [
{
"writtenAs": "upper",
"takes": [
"upper"
]
}
]
}

A helper library in the bundle could not be checked: the sandbox did not answer (code: SANDBOX_UNAVAILABLE). Nothing was written. Or the restore switch is on (code: MAINTENANCE): every route but /health and /robots.txt is refused before anything is read, with Retry-After (the operator’s ADMIN_TOKEN alone is let through to POST /staff/people/{id}/delete, and to nothing else).

Media typeapplication/json
One of:

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
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