Organisaties beheren via de API¶
Het concept achter organisaties staat in Organisaties en klanten.
Op deze pagina beheer je met een API-sleutel je eigen organisatie en de organisaties eronder: een organisatie voor een klant aanmaken, organisaties lezen en hernoemen, en sleutels aanmaken of intrekken. Je gebruikt het wanneer je klanten vanuit je eigen systeem inricht in plaats van met de hand.
Je sleutel heeft de capability management nodig (Aanzetten). Hetzelfde doe je met
de muis in Organisaties beheren in de console.
De capability geeft geen toegang tot de bestanden, prompts, skills of kennis van een onderliggende organisatie. Maak daar een sleutel aan en gebruik die om met de gegevens van die organisatie te werken. Beheerrechten en toegang tot gegevens worden apart gecontroleerd.
Aanzetten¶
Je organisatie moet management toestaan en de sleutel moet hem aanzetten. Een beheerder doet
allebei in de console (Toegestane capabilities,
Capabilities); een sleutel die management al heeft,
registreert een volgende sleutel ermee in de body:
{ "name": "Platformautomatisering", "capabilities": { "management": {} } }
De capability heeft geen configuratie — een leeg object is het hele verhaal. Kijk met GET /key wat
je eigen sleutel heeft. Zonder management antwoordt elke route op deze pagina 404, hetzelfde
antwoord als een onbekend pad (Een 404 oplossen).
De routes komen in paren. Een pad zonder organisatie erin gaat over je eigen organisatie —
GET /organisation, POST /keys, GET /admins — en een pad dat er een noemt gaat over een
organisatie onder je: GET /organisations/{id}, POST /organisations/{id}/keys. Je eigen id
voluit geschreven antwoordt hetzelfde als het korte pad.
Beheerrechten¶
Je sleutel beheert zijn eigen organisatie en elke organisatie eronder, en dat zijn twee verschillende taken. De organisaties die je te beheren kreeg, bestuur je; de organisatie die je zelf bent, kun je niet opschorten, verhangen of verwijderen.
| Handeling | Je eigen organisatie | Een organisatie onder je |
|---|---|---|
lezen (GET /organisation, GET /organisations/{id}) |
ja | ja |
name, timezone, data_retention_days, trace_retention_days, memory_retention_days, tracing_enabled, routing_config_id, assist_model, analysis_model, metadata |
ja; routing_config_id, assist_model en analysis_model tenzij het sleutelbeheer is voorbehouden |
ja |
inheritable_keys (welke sleutels van metadata de organisaties eronder lezen) |
ja | ja |
computer_internet (of de computers ervan internet mogen bereiken) |
ja; 403 zolang een organisatie erboven het uit heeft staan |
ja; hetzelfde |
alleen haar eigen document lezen (GET /organisation/metadata, GET /organisation/metadata/effective) |
ja -- en heeft geen management nodig |
— |
haar geldende document lezen (GET /organisations/{id}/metadata/effective) |
— | ja |
status, permitted_capabilities, admin_restrictions, monthly_cost_cap_eur, monthly_cost_cap_until (het einde van deze maand, voor een limiet voor alleen deze maand), monthly_cost_cap_counts (branch of own: of de limiet de organisaties eronder meetelt), monthly_cost_cap_counts_erased (true of false: of een limiet die de organisaties eronder meetelt, blijft tellen wat een verwijderde organisatie deze maand verbruikte) |
403 met het veld erbij |
ja; een plafond mag nooit verder gaan dan dat van de organisatie waar ze direct onder hangt |
parent_id, audit_retention_days, legal_hold |
403 met het veld erbij |
403; alleen Artific, waar de organisatie ook hangt |
| er een organisatie onder aanmaken | ja | ja |
| verwijderen | 403 |
ja |
| haar sleutels opvragen en lezen | ja | ja |
| haar sleutels registreren, bewerken, roteren en verwijderen | ja, tenzij het sleutelbeheer is voorbehouden | ja |
| haar beheerders opvragen | ja | ja |
| beheerders uitnodigen, opnieuw uitnodigen en verwijderen | 403 |
ja |
haar policy lezen en schrijven (GET/PUT/DELETE /organisation/policy, GET/PUT/DELETE /organisations/{id}/policy) |
ja; schrijven tenzij het sleutelbeheer is voorbehouden | ja |
de gepubliceerde versies van haar policy lezen (GET /organisation/policy/versions, GET /organisation/policy/versions/{version}, en hetzelfde onder /organisations/{id}/policy/versions) |
ja | ja |
een beleidsdocument testen voordat je het schrijft (POST /organisation/policy/decisions, POST /organisations/{id}/policy/decisions; Een beleidsdocument testen) |
ja, tenzij het sleutelbeheer is voorbehouden | ja |
haar labelwoordenlijst lezen en schrijven (GET/PUT/DELETE /organisation/label-vocabulary, GET/PUT/DELETE /organisations/{id}/label-vocabulary) |
lezen met elke sleutel; schrijven tenzij het sleutelbeheer is voorbehouden | ja |
haar identity providers lezen en schrijven (GET/PUT /organisation/subject-issuers) |
ja; schrijven tenzij het sleutelbeheer is voorbehouden | — |
haar huisstijlafbeeldingen opvragen, vullen en leegmaken (GET /organisation/assets, PUT/DELETE /organisation/assets/{slot}; Huisstijlafbeeldingen) |
ja | — |
haar toegestane hosts lezen en wijzigen (GET/POST /organisation/allowed-hosts, DELETE /organisation/allowed-hosts/{host}; Toegestane bestemmingen) |
ja; wijzigen geeft 403 als het sleutelbeheer is voorbehouden |
— |
haar e-maildomeinen en inlogproviders lezen en schrijven (/organisation/email-domains, /organisation/sign-in-providers) |
ja (Een eigen inlogprovider instellen met een sleutel) | — |
haar levensloop lezen (GET /control-events, GET /organisations/{id}/control-events) |
ja | ja |
De regels over identity providers en toegestane hosts gaan over je eigen organisatie en niet over
een onderliggende: de providers waarvan je een token mag meesturen
(Bouwen voor personen) en de hosts die je eigen aanroepen
bereiken. Wat een onderliggende organisatie daar heeft staan, lees je met een sleutel die je daar
registreert. Haar beleidsdocument lees en schrijf je van bovenaf, op /organisations/{id}/policy:
stuur een heel document, net als voor je eigen organisatie, en begin bij
GET /organisation/policy/default. De hosts die een onderliggende organisatie mag bereiken, zijn
de regels permit van de soort egress_host in dat document; de regels van dezelfde soort in je
eigen document, en in de documenten boven je, begrenzen haar ook
(Policy). Noem bij het schrijven de versie die je las in If-Match,
zodat je geen wijziging overschrijft die iemand na je leesactie heeft opgeslagen
(Sleutels uitgeven, roteren en intrekken).
Versies en concepten van de policy. Elke PUT en DELETE van een beleidsdocument wordt meteen
gepubliceerd als de volgende genummerde versie, en een sleutel leest de gepubliceerde versies op
/organisation/policy/versions. Een concept is van een persoon, alleen op de beheertweeling:
POST /tenants/{id}/policy/versions met document en "draft": true schrijft er een,
POST /tenants/{id}/policy/versions/{version}/publish zet het in werking en
DELETE /tenants/{id}/policy/versions/{version} gooit het weg; het schrijven en het publiceren
noemen de version van het gepubliceerde document in If-Match. POST /tenants/{id}/policy/versions/{version}/decisions
test een versie met een sleutel van de organisatie en hoogstens 50 vragen, beantwoord onder die
versie en onder het gepubliceerde document; het beslist niets echt en schrijft niets. Geen sleutel
leest een concept, en geen verzoek wordt eronder beoordeeld (Policy).
Een sleutel test in plaats daarvan een document dat hij zelf meestuurt
(Een beleidsdocument testen voordat je het schrijft).
Een weigering op zo'n veld is een problem-document dat het veld noemt, bijvoorbeeld Only an
operator or a parent organisation's administrator may change 'status'. Er wordt nooit een deel
toegepast: een body met één veld dat je niet mag zetten, verandert er geen enkel.
Alles buiten je bereik antwoordt 404. Een organisatie die niet van jou is en niet onder je
hangt, is niet te onderscheiden van een organisatie die niet bestaat, op welke route je haar ook
noemt. Zo valt de boom niet af te lopen op zoek naar id's.
Het verbruik, het auditspoor en de routeconfiguraties van een organisatie onder je bereik je met dezelfde sleutel: zie Verbruik, audit en routeconfiguraties van een organisatie lezen. De prijslijst van de installatie niet: die is van Artific, en je organisatie leest een prijs en schrijft er nooit een.
Je eigen organisatie lezen en hernoemen¶
curl -s "$CORE_URL/organisation" -H "Authorization: Bearer $CORE_KEY"
Het antwoord is de organisatie zoals de console haar toont: haar id en naam, waar ze hangt
(parent_id), hoe ze gescheiden wordt gehouden (isolation) en op welke database ze staat
(database_ref), de capabilities die ze toestaat, haar limieten, haar bewaartermijnen en wat de
organisatie erboven zich heeft voorbehouden.
PATCH /organisation wijzigt de velden uit de tabel hierboven die je eigen organisatie mag zetten:
curl -s -X PATCH "$CORE_URL/organisation" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"name": "Noordwind Handel", "data_retention_days": 90}'
data_retention_days is de termijn over de eigen inhoud van je organisatie — gesprekken,
antwoorden, bestanden, artefacten en geheugenregels — en null bewaart die voorgoed
(Gegevensbewaring). Het is de beslissing van je
organisatie over haar eigen gegevens, en daarom mag je sleutel hem zetten terwijl hij de termijn over
het auditspoor niet mag aanraken. routing_config_id noemt een van de fallback-configuraties van je
eigen organisatie, of null voor geen (Terugvallen op een ander model).
tracing_enabled zegt of Core vastlegt wat het voor je organisatie naar een model stuurt, en
trace_retention_days geeft traces een eigen, kortere termijn dan data_retention_days
(Traces). memory_retention_days geeft geheugenrecords een eigen
termijn, langer of korter. Daaronder telt een record vanaf zijn laatste schrijf- of
bevestigingsmoment, dus een feit dat je blijft bevestigen blijft staan en een open toezegging wordt
nooit verwijderd; null laat geheugen onder data_retention_days, geteld vanaf het aanmaken van
elk record. assist_model noemt het model achter Beschrijf wat je
nodig hebt, of null voor de standaard van de installatie (Hulpmodel).
Hetzelfde model beantwoordt elke beurt van je eigen helper
die geen model noemt.
analysis_model noemt het model van verbeteranalyses, met dezelfde regels, en valt nooit terug op
assist_model (Het analysemodel).
computer_internet zegt of de computers van je organisatie internet mogen bereiken
(Computers). Een nieuwe organisatie
onder de jouwe begint met de schakelaar aan als die voor de jouwe telt, en anders uit; stuur
"computer_internet": false bij het aanmaken om haar toch uit te laten beginnen. Daarna beslist
je organisatie het zelf: je sleutel mag het aanzetten, en iedereen boven je ook. Het telt alleen
zolang elke organisatie boven de jouwe het ook aan heeft staan, dus zet je het bij jou uit, dan
staat het uit voor elke organisatie onder je, en aanzetten onder een organisatie die het uit heeft
staan is 403. Uitzetten beëindigt niet wat al draait: een computer houdt zijn open netwerk tot
zijn levensduur voorbij is, de tunnels die hij via de proxy van Core open heeft sluiten bij de
volgende controle van de proxy (ongeveer elke 30 seconden), en DELETE /computers/{computer_id}
beëindigt hem eerder. Het antwoord bevat computer_internet_in_force, of het op dit moment telt,
en computer_internet_available, of deze omgeving zulke computers überhaupt aanbiedt; zolang dat
false is, wordt de schakelaar bewaard en doet hij niets.
curl -s -X PATCH "$CORE_URL/organisation" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"computer_internet": true}'
Zet het alleen aan als je accepteert wat het betekent: code die een model of script daar draait, kan wat het meekreeg, bestanden inbegrepen, naar elke host op internet sturen.
timezone is een IANA-zonenaam zoals Europe/Amsterdam, waarin de ingebouwde datum- en
tijdwaarden van je prompts worden gelezen als een verzoek of planning geen eigen zone noemt
(Tijdzone). Een nieuwe organisatie staat op UTC; stuur
"UTC" om daarheen terug te gaan, want null laat de zone staan. Een zone die Core niet kent, is
422 op timezone.
metadata is een eigen ondoorzichtig object, maximaal 16 KiB geserialiseerd -- Core bewaart het,
geeft het terug en leest het nooit. Hier bewaart een platform gebouwd op Core zijn eigen huisstijl:
kleuren, tekst, wat het nodig heeft; de afbeeldingen gaan naar Huisstijlafbeeldingen.
Laat het veld weg om te houden wat er staat, en stuur {} om het te wissen.
Elke lezing van je organisatie geeft er metadata_version naast. Stuur die terug in If-Match
bij de PATCH, en Core weigert de hele wijziging met 409 (reason_code: "stale_version") als
iemand metadata na jouw lezing heeft veranderd, zonder iets te schrijven; lees opnieuw en herhaal
de wijziging. Zonder de header vervangt de PATCH wat er staat:
curl -s -X PATCH "$CORE_URL/organisation" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-H 'If-Match: "3f9a..."' \
-d '{"metadata": {"theme": {"primary": "#0a5"}}}'
Er gaat niets van naar beneden tenzij je dat zegt. inheritable_keys noemt de sleutels op het
hoogste niveau van je metadata die de organisaties onder de jouwe als hun eigen mogen lezen --
hoogstens 32, elk één keer, als geheel vervangen, [] voor geen. Een sleutel die je niet noemt
blijft van jou alleen:
curl -s -X PATCH "$CORE_URL/organisation" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"metadata": {"theme": {"primary": "#0a5"}, "billing_note": "intern"},
"inheritable_keys": ["theme"]}'
GET /organisation/metadata/effective geeft in één aanroep het geldende document van een
organisatie: haar eigen sleutels, plus elke sleutel die ze mist van de dichtstbijzijnde organisatie
boven haar die die sleutel noemt én heeft. Een organisatie erboven die een sleutel noemt maar niet
heeft, wordt overgeslagen, en de organisatie daarboven wordt gevraagd.
{"metadata": {"logo_text": "Northwind", "theme": {"primary": "#0a5"}},
"inherited_from": {"theme": null}}
inherited_from noemt voor elke sleutel die van boven kwam de organisatie waar hij vandaan kwam --
of null als je credential die organisatie niet bereikt. Een sleutel van je eigen organisatie
bereikt nooit wat erboven ligt, dus op deze route is elke waarde null; lees
GET /organisations/{id}/metadata/effective met een sleutel hoger in de boom om de id's te zien.
Een geschorste organisatie erboven leent haar sleutels nog steeds uit; een verwijderde leent niets
uit, en wat erboven ligt ook niet.
Anders dan al het andere in dit hoofdstuk hebben GET /organisation/metadata en
GET /organisation/metadata/effective geen management nodig: elke sleutel van je organisatie
leest ze, en een subject-token dat tot één persoon is versmald ook, zodat een browsergerichte frontend zijn eigen huisstijl kan lezen met welke
credential hij ook heeft gekregen (Bouwen voor mensen).
Een organisatie onder je aanmaken¶
De naam van een organisatie is uniek binnen de organisaties onder dezelfde bovenliggende; aanmaken, hernoemen of verplaatsen naar een naam die een zusterorganisatie al heeft, wordt met 409 geweigerd. Dubbele namen die al bestaan blijven staan totdat iemand ze hernoemt.
POST /organisations noemt de bovenliggende organisatie, en dat is elke organisatie die je al
bereikt — je eigen, of een die verder naar beneden hangt:
curl -s -X POST "$CORE_URL/organisations" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"name": "Noordwind Retail", "parent_id": "ten_4Kd9...",
"permitted_capabilities": ["threads", "knowledge", "management"]}'
| Veld | Wat je stuurt |
|---|---|
name |
hoe de organisatie heet; het enige verplichte veld |
parent_id |
waaronder ze komt te hangen. Laat je het weg of stuur je null, dan vraag je om een organisatie die onder niets hangt, en die maakt alleen Artific aan (403) |
permitted_capabilities |
het plafond voor elke sleutel die deze organisatie krijgt. Het moet binnen het plafond blijven van de organisatie die je als bovenliggende noemde; de eerste naam erbuiten wordt geweigerd met '<name>' is outside the capability ceiling of the organisation this one sits under. |
isolation |
shared, tenzij je iets anders is verteld. schema geeft de nieuwe organisatie een eigen databaseschema en -rol, en die keuze is van jou, wat je eigen organisatie ook gebruikt |
database_ref |
stuur het niet. Een database noemen is alleen aan een operator, en een sleutel is nooit een operator; de nieuwe organisatie staat standaard op dezelfde fysieke database als de organisatie die je als bovenliggende noemde. Het veld meesturen wordt geweigerd met 403, ook als het diezelfde database noemt |
Het antwoord is 201 met de nieuwe organisatie en een Location-header. Haar status is
provisioning zolang Core haar inricht; vraag GET /organisations/{id} op tot er active staat. Je
kunt er meteen sleutels in registreren; ze beantwoorden aanroepen zodra ze actief is
(wat de statussen betekenen).
Tot die tijd antwoordt een aanroep met een van haar sleutels 409 problem+json met de titel
Conflict en de detail The tenant is not ready., zonder header Retry-After (de modelroutes
geven dezelfde melding in het foutobject van de leverancier):
probeer het even later opnieuw.
Wat je zojuist hebt aangemaakt, beheer je vanaf het moment dat deze aanroep antwoordt.
status is altijd die van de organisatie zelf. Een organisatie opschorten legt ook elke
organisatie eronder stil, en zo'n organisatie houdt status: "active": lees suspended_by_ancestor
ernaast. true betekent dat een organisatie erboven is opgeschort, zodat haar sleutels 403
antwoorden en haar runs, schema's en kenniswerk stilliggen tot die organisatie weer geactiveerd is
(Opschorten). Het veld zegt niet welke organisatie het is.
permitted_capabilities is op dezelfde manier het eigen opgeslagen plafond van de organisatie, de
waarde die jij schrijft. Een versmalling erboven verandert het niet: lees capabilities_in_force
ernaast, de capabilities die haar sleutels en beheerders nu mogen gebruiken, zonder wat een
organisatie erboven niet meer toestaat, pseudonymisation en trust_envelope inbegrepen. Het veld
is alleen te lezen en zegt niet welke organisatie een capability tegenhoudt.
DELETE /organisations/{id} wist een organisatie onder je en alles wat ze bezit, op de achtergrond,
met status op deleting tot ze weg is. Zolang ze wordt verwijderd, antwoorden haar sleutels
dezelfde 409; is ze weg, dan zijn haar sleutels mee verdwenen en antwoorden ze 401. Een
organisatie waar nog levende organisaties onder hangen
wordt geweigerd met 409: wissen werkt nooit door naar beneden, dus die wis je eerst. Je eigen
organisatie levert 403 op — een organisatie wist zichzelf niet.
Werken in een organisatie onder je¶
Deze routes beheren organisaties, nooit hun inhoud. Wil je de kennis van een onderliggende organisatie lezen, er een prompt opslaan of namens haar een beurt versturen, registreer dan een sleutel in die organisatie en roep aan als die sleutel:
curl -s -X POST "$CORE_URL/organisations/ten_9Fb2.../keys" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"name": "Retail-ingest", "capabilities": {"knowledge": {}}}'
De secret staat in dat antwoord en nergens anders. Een sleutel van de onderliggende organisatie
bereikt haar eigen gegevens en niets daarboven: GET /key noemt die organisatie, en vraag je die
sleutel naar de organisatie die hem aanmaakte, dan is het antwoord 404. Geef je hem ook
management, dan beheert hij die organisatie en alles wat er wéér onder hangt; zo zakt een
applicatie een niveau dieper.
Sleutels uitgeven, roteren en intrekken¶
/keys gaat over je eigen organisatie en /organisations/{id}/keys over een organisatie eronder.
Op allebei zitten dezelfde zeven handelingen: opvragen, registreren, lezen, bewerken, roteren, het
callbackgeheim aanmaken of vervangen
(POST …/keys/{key_id}/callback-secret) en verwijderen.
curl -s -X POST "$CORE_URL/keys" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"name": "Afrekenservice", "capabilities": {"threads": {}}, "monthly_cost_cap_eur": "250.00"}'
- De secret verschijnt één keer.
secretstaat in het antwoord op de registratie en op de rotatie en in geen enkel ander antwoord; Core bewaart de hash en de twaalf tekens vansecret_prefix. Raak je hem kwijt, roteer dan. - Wat je mag aanzetten is het plafond van je organisatie,
managementinbegrepen. Een capability daarbuiten antwoordt409metcapability '<name>' is not permitted for this tenant, en een capability waarvan de afhankelijkheid er niet naast aanstaat ook:memoryvraagtknowledge,runsvraagtthreads,artifactsvraagtfiles,evaluationsvraagtagents. - Roteren beëindigt de vorige secret meteen.
POST /keys/{key_id}/rotateantwoordt met de nieuwe; er is geen periode waarin allebei werken. De aanroepende sleutel mag zichzelf roteren, en daarmee beëindigt hij de sleutel die de aanroep deed: het antwoord is het laatste wat de oude secret oplevert. - Je mag de secret zelf meebrengen. Registratie en rotatie nemen een optionele
secretin de body ({"secret": "ack_..."}op de rotatieroute), met precies de vorm die Core zelf uitgeeft:ack_en 43 URL-veilige base64-tekens, 32 willekeurige bytes uit een veilige generator. Een andere vorm antwoordt422opsecret, en een secret die een sleutel al heeft ook. Het antwoord bevat hem één keer, net als een gegenereerde; laat het veld weg en Core genereert hem. Terraform gebruikt dit, zodat de secret van een sleutel nooit in zijn state belandt. PATCH /keys/{key_id}versmalt een sleutel, ook zichzelf — zo neemt een applicatie zichzelf capabilities af voordat ze een beurt iets riskants aanbiedt. Zijn eigen maandlimiet is de uitzondering:monthly_cost_cap_eurvoor de aanroepende sleutel zelf antwoordt403met het veld erbij, ook metnull, want een sleutel bepaalt nooit zijn eigen plafond. Een andere sleutel metmanagement, of een beheerder in de console, stelt hem in.- Een schrijfactie op een policy kan de versie noemen die ze las. Een beleidsdocument wordt in
zijn geheel vervangen, dus van twee schrijvers die het allebei lazen en allebei schrijven,
verwijdert de tweede ongemerkt de wijziging van de eerste. Elke leesactie van een sleutel
antwoordt
policy_version, en elke leesactie van de policy van een organisatie antwoordtversion; ook een organisatie zonder document heeft eenversion. Stuur die waarde mee in de headerIf-Matchbij het schrijven (PATCHvan een sleutel,PUTofDELETEvan de policy van een organisatie) en Core weigert met409als het opgeslagen document intussen is gewijzigd. Er wordt niets geschreven: lees opnieuw en herhaal je wijziging. Zonder de header vervangt de schrijfactie wat er staat.- De body van die weigering draagt
reason_code: "stale_version". Schakel op dat veld en niet op de status: dezelfde routes antwoorden ook om andere redenen409, en die bodies dragen geenreason_code. - Een
PATCHvan een sleutel metIf-Matchwordt in zijn geheel geweigerd als het document van de sleutel is gewijzigd, ook als de body geenpolicybevat. If-Match: *komt overeen met wat er ook is opgeslagen.- Een toegestane host toevoegen of verwijderen wijzigt de
versionvan de organisatie: de lijst met hosts is een deel van hetzelfde document.
- De body van die weigering draagt
expires_atlaat een sleutel verlopen zonder haar te verwijderen. Zet hem bij registratie of via eenPATCH, altijd in de toekomst — een moment in het verleden antwoordt422. Stuurnullop eenPATCHom een bestaande vervaldatum op te heffen; laat je het veld weg, dan blijft staan wat de sleutel al had. Voorbij dat moment wordt de sleutel geweigerd zoals een onbekende sleutel —401, hetzelfde antwoord — of ze nu als secret komt of als subject-token dat erop is gemunt. Roteren zet dit nooit terug: roteren vervangt de secret, niet de levensduur.- Een sleutel overleeft nooit de sleutel die haar registreert of bewerkt. Zodra je eigen
sleutel een
expires_atdraagt, moet elke sleutel die ze aanmaakt of bewerkt — zichzelf inbegrepen — er ook een dragen, nooit later dan die van haarzelf: je mag de vervaldatum van je eigen sleutel verkorten, of er een zetten waar er nog geen was, maar niet verlengen of opheffen, en dat geldt net zo goed voor elke sleutel die je uitgeeft. Een ruimere waarde versturen antwoordt403, niet422— de waarde is niet onjuist, een operator of beheerder mag dezelfde waarde zetten zonder geweigerd te worden. Een sleutel zonder eigen vervaldatum legt niemand een plafond op. DELETEstopt verdere aanroepen met die sleutel en beëindigt meteen elk subject-token dat ermee is gemaakt (Een token voor de browser). Wat ermee is aangemaakt, blijft van de organisatie.
De console toont en doet hetzelfde (Sleutels); een sleutel die je vanuit de API registreert, is daar een gewone rij.
Verbruik, audit en routeconfiguraties van een organisatie lezen¶
Zeven routes onder /organisations/{id}/... beantwoorden voor een organisatie precies wat
/tenants/{id}/... voor een beheerder beantwoordt: GET .../usage, .../usage/summary,
.../usage/caps, .../usage/by-subject, .../usage/by-key, .../audit/events en
.../audit/events/export. Op .../usage/by-key na nemen ze dezelfde parameters (key_id,
from, to, interval=day op de samenvatting; limit op de by-subject-lezing) en antwoorden ze
dezelfde vorm als de routes in Gebruik en audit via de API. Er is geen los pad voor
je eigen organisatie: /organisations/{je eigen id}/usage werkt net zo goed, want je sleutel
bereikt haar eigen organisatie als een van haar eigen wortels (Beheerrechten).
GET .../usage/by-key heeft geen tegenhanger onder /usage: het zegt per sleutel wat die kostte.
Het neemt alleen from en to; noem je geen van beide, dan is het venster de lopende
kalendermaand in UTC. items heeft één regel per sleutel met verbruik in het venster, met
key_id, rows, cost_eur en unpriced, gesorteerd op kosten aflopend; een sleutel die niets
verbruikte, staat er niet in. keyless is dezelfde regel voor wat zonder sleutel binnenkwam, zoals
wat iemand vanuit de console startte, met key_id null, en staat er altijd, op nul als er niets
was. cost_eur bovenaan is het totaal van alle regels samen. Een unpriced boven nul betekent dat
het bedrag een ondergrens is.
curl -s "$CORE_URL/organisations/ten_9Fb2.../usage/summary?interval=day&from=2026-09-01T00:00:00Z&to=2026-10-01T00:00:00Z" \
-H "Authorization: Bearer $CORE_KEY"
Routeconfiguraties werken op dezelfde twee paden: /routing-configs is je eigen organisatie,
/organisations/{id}/routing-configs een organisatie eronder — opvragen, aanmaken, lezen,
bewerken en verwijderen, en de gepubliceerde versies van een configuratie opvragen en lezen
(.../{config_id}/versions), zoals /tenants/{id}/routing-configs een beheerder biedt, waar
aanmaken en bewerken ook draft: true nemen en alleen de beheerder een concept publiceert of
weggooit (Terugvallen op een ander model):
curl -s -X POST "$CORE_URL/organisations/ten_9Fb2.../routing-configs" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"name": "default", "chains": [{"primary": "gpt-5.6-sol", "fallbacks": ["claude-sonnet-5"]}]}'
Een beleidsdocument testen voordat je het schrijft¶
Vraag, voordat je een beleidsdocument met PUT schrijft, hoe het zou beslissen. Stuur het document
dat je wilt schrijven mee, het id van een sleutel van die organisatie en hoogstens 50 vragen: een
soort, een naam, een action waar de soort er een kent, de labels die het ding draagt en, voor
een persoon, haar attributes. Het document is een heel document, net als bij een PUT: een soort
waarover het geen regel schrijft, wordt geweigerd, dus begin bij GET /organisation/policy/default.
Het voorbeeld hieronder is om die reden ingekort.
curl -s -X POST "$CORE_URL/organisation/policy/decisions" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"key_id": "key_01K...",
"document": {"rules": [
{"effect": "permit", "kind": "prompt", "match": {"names": ["private-*"]}},
{"effect": "permit", "kind": "egress_host", "match": {"names": ["api.jansen.nl"]}}]},
"questions": [{"kind": "prompt", "name": "private-welcome", "action": "use"},
{"kind": "egress_host", "name": "api.jansen.nl"}]}'
Elk antwoord zegt onder draft wat het meegestuurde document zou beslissen, en onder published
wat het document dat nu geldt beslist, met allowed, de reason, welk document besliste
(organisation, key, ancestor of default) en de rule. Een host wordt beantwoord zoals een
uitgaande aanroep hem zou beoordelen, en niemand belt hem. Voor een organisatie onder je gaat
dezelfde body naar POST /organisations/{id}/policy/decisions, met een sleutel van die organisatie
als key_id.
De test weigert wat de PUT zou weigeren, met hetzelfde antwoord: een label buiten de woordenlijst
van de organisatie is 422 met hetzelfde veld, net als een live check die een beoordelingsmodel
noemt dat de organisatie niet kan gebruiken, en voorbehouden sleutelbeheer is dezelfde 403. Hij
schrijft niets: geen versie, geen gebeurtenis, geen concept. Een opgeslagen concept is van een
persoon en alleen de beheerconsole test er een.
Voorbehouden sleutelbeheer¶
De organisatie boven je kan het sleutelbeheer aan zichzelf voorbehouden
(Voorbehouden). Dat bindt je sleutel net zo goed als je
beheerders: registreren, bewerken, roteren en verwijderen op je eigen organisatie antwoorden 403
met This organisation's parent has reserved key management to itself., en het opvragen en lezen van
je sleutels blijft gewoon van jou. routing_config_id zetten wordt om dezelfde reden geweigerd, en
zo ook elke schrijfactie op /routing-configs van je eigen organisatie: dat is allemaal
sleutelconfiguratie.
Onder je wordt niets ingehouden, want daar bén jij de organisatie erboven.
Verbruikscijfers of het auditspoor voorbehouden bindt je sleutel op dezelfde manier: GET
/organisations/{je eigen id}/usage en de andere vijf antwoorden 403 zodra je eigen organisatie
het is voorbehouden, en gewoon 200 voor een organisatie eronder. GET /usage,
GET /audit/events en GET /key — je sleutel die naar haar eigen gegevens vraagt zonder een
organisatie te noemen — raakt een voorbehoud nooit.
Wie een organisatie beheert¶
GET /admins geeft de mensen die je eigen organisatie beheren, met hun status. Meer biedt je eigen
organisatie niet: wie haar beheert, wordt boven haar beslist, dus een sleutel kan niemand uitnodigen
in de organisatie die hem heeft uitgegeven.
Onder je is de hele set van jou:
| Aanroep | Wat hij doet |
|---|---|
GET /organisations/{id}/admins |
wie die organisatie beheert. Een lege pagina is een echt antwoord |
POST /organisations/{id}/admins |
iemand uitnodigen op e-mailadres. created zegt of er een account is gemaakt, invite_sent of de mail eruit ging |
POST /organisations/{id}/admins/{user_id}/invite |
de uitnodiging opnieuw versturen, en herstellen wat er halverwege bleef steken |
DELETE /organisations/{id}/admins/{user_id} |
die persoon het beheer over die organisatie afnemen. Het account zelf blijft ongemoeid |
Iemand met active: false heeft nog niets geaccepteerd of is halverwege gestrand; de uitnodiging
opnieuw versturen is de reparatie (Beheerders).
Beheergebeurtenissen¶
GET /control-events geeft wat er met je eigen organisatie is gebeurd: haar eigen aanmaak, haar
hernoemingen en gewijzigde bewaartermijnen, haar sleutels die zijn geregistreerd, gewijzigd,
geroteerd en verwijderd, haar beheerders die zijn uitgenodigd en verwijderd, en haar
exports die zijn aangevraagd en opgehaald. Elke gebeurtenis
draagt event, created_at, request_id en actor — het id van je eigen sleutel als een sleutel
het deed, of operator:… als een persoon het deed.
Een gebeurtenis over een organisatie onder je hoort bij háár spoor en niet bij het jouwe: de
organisatie die je aanmaakte, vindt haar eigen aanmaak op haar eigen spoor. Dat spoor lees je van
bovenaf met GET /organisations/{id}/control-events, met dezelfde limit, cursor en order en
hetzelfde antwoord; een organisatie buiten je bereik is 404.
Een organisatie erboven die je auditspoor heeft voorbehouden, heeft dit niet voorbehouden: haar eigen levensloop is het ene overzicht dat een organisatie altijd houdt. Dezelfde gebeurtenissen zijn de Recente gebeurtenissen van de console en haar beheerauditgeschiedenis (Audit).
Huisstijlafbeeldingen¶
Je organisatie kan tot 250 afbeeldingen in Core bewaren — een logo, een favicon, een afbeelding
voor een inlogpagina, een licht en een donker logo voor elk thema van een ingebedde assistent — en
ze overal laten zien, ook aan mensen die geen sleutel hebben. Elke afbeelding staat in een slot,
een naam die je zelf kiest: een kleine letter, dan tot 31 kleine
letters, cijfers of streepjes (logo, favicon, sign-in). Core leest de naam nooit.
Zet een afbeelding in een slot door het bestand zelf als body te sturen. Core accepteert PNG, JPEG
en WebP tot 512 KiB, en bepaalt het type aan de bytes van het bestand, niet aan de Content-Type
die je meestuurt. SVG wordt niet geaccepteerd.
curl -s -X PUT "$CORE_URL/organisation/assets/logo" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: image/png" \
--data-binary @logo.png
{"object": "organisation_asset", "slot": "logo", "content_type": "image/png",
"size_bytes": 18342, "sha256": "9f2c...e41a", "url": "/api/v1/assets/9f2c...e41a",
"created_at": "2026-10-17T09:12:00Z", "updated_at": "2026-10-17T09:12:00Z"}
Het antwoord is 200, of het slot nu leeg was of al een afbeelding had, die de nieuwe dan
vervangt. GET /organisation/assets geeft elk slot met dezelfde velden en zonder de bytes, en
DELETE /organisation/assets/{slot} maakt er een leeg; dat antwoordt 204, ook als het slot al
leeg was.
| Antwoord | Betekenis | Wat je doet |
|---|---|---|
413 |
de afbeelding is groter dan 512 KiB | maak hem kleiner |
415 |
de bytes zijn geen PNG, JPEG of WebP | zet het bestand om; een SVG moet je eerst als PNG of WebP opslaan |
422 |
de body is leeg, of de slotnaam past niet in het patroon | stuur het bestand mee, of gebruik DELETE om een slot leeg te maken |
409 |
de organisatie vult al 250 slots, of wordt nog aangemaakt of verwijderd | maak eerst een slot leeg, of wacht tot de organisatie klaar is |
Het openbare adres¶
url is waar iedereen de afbeelding leest, zonder sleutel en zonder token. Zet je Core-host ervoor,
zonder de /api/v1 uit CORE_URL, want die zit al in url:
https://core.example.com/api/v1/assets/9f2c...e41a. Dat adres gebruik je zo in een <img> op een
inlogpagina, in een mail of in je eigen frontend, op elk domein.
Het adres is genoemd naar de inhoud van de afbeelding, niet naar je organisatie, dus het zegt niets
over wie hem uploadde. Daardoor verandert het ook zodra de afbeelding verandert: vervang je het
logo, lees dan de nieuwe url en gebruik die. Browsers en andere caches mogen een afbeelding een
jaar bewaren (Cache-Control: public, max-age=31536000, immutable), dus gebruik nooit een oud adres
voor een nieuwe afbeelding; een nieuwe afbeelding krijgt altijd een nieuw adres. Een lezer die de
ETag die hij kreeg in If-None-Match meestuurt, krijgt 304 en geen bytes.
Een adres antwoordt 404 zodra geen organisatie die afbeelding nog heeft: nadat je het slot leeg
hebt gemaakt of de afbeelding hebt vervangen, tenzij een andere organisatie toevallig hetzelfde
bestand bewaart.
Elke afbeelding is van je eigen organisatie: een organisatie onder de jouwe erft er geen en vult
haar eigen slots met een eigen sleutel
(Werken in een organisatie onder je). Een slot vullen en
leegmaken staan in je beheergebeurtenissen als tenant.asset_set en
tenant.asset_deleted. Het archief dat je meeneemt bevat de afbeeldingen,
en de slots opvragen en leegmaken zijn tools op het MCP-endpoint (list_own_assets,
delete_own_asset); een slot vullen niet, want de afbeelding is bytes en geen toolargument. Een
subject-token krijgt 403 op deze routes.
Verwijderen haalt kopieën elders niet terug. Als je een slot leegmaakt, of je organisatie wordt verwijderd, haalt Core zijn eigen kopie weg en antwoordt het adres niet meer. Een browser, een maildienst of een CDN die de afbeelding al ophaalde, kan zijn eigen kopie nog tot een jaar laten zien, en Core kan daar niet bij. Zet in een huisstijlafbeelding niets wat je misschien snel moet kunnen intrekken.
Je gegevens meenemen¶
Een organisatie die vertrekt, neemt een archief mee van alles wat Core voor haar bewaart. Vraag het aan, wacht tot het klaar is en haal het op:
curl -X POST "$CORE/api/v1/exports" -H "Authorization: Bearer $KEY" \
-H "Content-Type: application/json" -d '{}'
# 202, met Location: /api/v1/exports/exp_…
curl "$CORE/api/v1/exports/exp_…" -H "Authorization: Bearer $KEY"
# status: queued, running, dan ready
curl -o archief.zip "$CORE/api/v1/exports/exp_…/download" -H "Authorization: Bearer $KEY"
Het archief is een zip. manifest.json noemt het formaat en de versie, het aantal rijen per
bestand en wat er bewust niet in zit; organisation.json zijn de instellingen en het
beleidsdocument van je organisatie; tables/ heeft per soort gegeven één JSON Lines-bestand —
gesprekken met hun berichten, antwoorden, bestanden en artifacts, kennis, geheugen, prompts,
skills, agents, tools, evaluaties, schema's, het auditspoor en het verbruik — en blobs/ de bytes
van elk bestand. Je huisstijlafbeeldingen zitten er ook in, hun gegevens onder control/ en de afbeeldingen zelf onder files/. Geheimen zitten er nooit in, versleuteld of niet: de referenties van je
connectors en MCP-servers moet je bij je nieuwe leverancier opnieuw invoeren.
Stuur {"subject": "anna"} mee en het archief bevat alleen wat voor die ene persoon is
geschreven: zo beantwoord je een inzage- of overdrachtsverzoek (AVG artikel 15 en 20). Onder je
werkt hetzelfde op /organisations/{id}/exports. Een subject-token krijgt 403 op al deze
aanroepen: het archief vraag je aan vanuit de backend die de sleutel heeft.
Het archief is export_retention_hours lang op te halen, standaard 72 uur, en zo vaak als nodig;
daarna antwoordt de download 409 en vraag je een nieuw aan. Elke download is een gebeurtenis
tenant.export_downloaded, met wie hem ophaalde en hoeveel traces het archief bevatte, in de beheergebeurtenissen van je
organisatie (GET /control-events). Heeft de organisatie boven je het auditspoor of het verbruik
voorbehouden, dan laat jouw archief die rijen weg en zegt dat in withheld.
Een download die werd afgebroken, gaat verder waar hij stopte: stuur Range: bytes=<n>-, waarbij
<n> het aantal bytes is dat je al hebt (curl -C - -o archief.zip ... doet dat), en het antwoord
is 206 met de rest. Elk hervat verzoek is een download op zich, met een eigen gebeurtenis
tenant.export_downloaded. Een bereik dat op of voorbij het einde van het archief begint, krijgt
416 en wordt niet vastgelegd; curl -C - op een bestand dat al volledig binnen is, krijgt dat
antwoord.
Aanvragen, opvragen en de lijst zijn ook tools op het MCP-endpoint (create_own_export,
get_own_export, list_own_exports); de download niet, want een archief is bytes en geen
toolresultaat.
Beheren via MCP¶
Elke handeling op deze pagina is ook een tool op het sleutelendpoint, zonder dat je iets instelt
(Core gebruiken vanuit een MCP-client). De tool heet naar de
handeling: get_own_organisation, update_own_organisation, create_organisation,
register_organisation_key, rotate_own_organisation_key, invite_organisation_admin,
list_organisation_control_events. Een sleutel zonder management krijgt er geen enkele van te
zien.
Binnen een beurt die op Core draait, zijn het dezelfde tools met het voorvoegsel core__ —
core__create_organisation, core__register_organisation_key — zodat een agent een organisatie in
één beurt kan inrichten (Een agent het platform geven). Een
registratie antwoordt met de nieuwe secret, en een toolresultaat hoort bij de beurt: de secret komt
langs het model en blijft bewaard bij wat de beurt heeft opgeslagen. Registreer sleutels vanuit je
eigen applicatie als dat uitmaakt.
Geef management aan weinig sleutels¶
Een sleutel met management geeft weg wat een beheerder weggeeft: elke capability binnen het plafond
van je organisatie, management daarbij. Zo'n sleutel kan dus een tweede sleutel registreren die
evenveel beheert als hij. Die tweede sleutel is een gewone rij in de console en zijn registratie
staat in het spoor, dus er blijft niets verborgen — maar wie de eerste secret heeft, beslist erover.
Behandel die secret zoals je het wachtwoord van een beheerder behandelt.
In de praktijk betekent dat: één sleutel voor het beheer in plaats van management erbij op een
sleutel die ook beurten bedient, een maandlimiet erop zoals op elke andere sleutel, en roteren zodra
hij ergens is geweest waar hij niet hoorde. Elke handeling ervan staat in het spoor hierboven, onder
zijn eigen sleutel-id.