Ga naar inhoud

Fouten, paginering en herhalen

Core gebruikt in de hele API hetzelfde foutformaat en paginering met cursors. Met request-id's kun je problemen terugzoeken. Met een idempotentiesleutel kun je bij een herhaling een afgeronde aanroep terugvinden.

Het verzoekformaat is dat van OpenAI

De body van POST /responses is die van OpenAI's Responses-API. Een client die je vandaag naar OpenAI stuurt, stuur je ongewijzigd naar Core: je verandert het adres in je basis-URL en de sleutel in je secret, en verder niets aan wat je verstuurt.

Core neemt in die body ook een paar optionele eigen velden aan, zoals agent, timezone, tool_search, tool_variables en hosting. Verzoekvelden noemt ze; een verzoek dat ze weglaat, gedraagt zich zoals bij OpenAI. POST /messages neemt op dezelfde manier de body van Anthropic met de eigen velden van Core (Een codeeragent aansluiten).

Wat Core daarbovenop biedt, reist in headers. De belangrijkste is X-Core-Subject, de persoon voor wie een beurt is: zet die één keer op je client en elk verzoek draagt hem.

client = OpenAI(base_url=CORE_URL, api_key=CORE_KEY, default_headers={"X-Core-Subject": subject_json})

Dat het een header is en geen veld in de body of in de query, is opzet: attributen zijn het deel van een verzoek dat geen enkele proxy onderweg in een logregel mag zetten. Zie Bouwen voor personen voor wat je erin zet, en user als je alleen het id kwijt wilt.

Header Verstuurd door Wat hij draagt
X-Core-Subject jou de persoon voor wie een verzoek is, met haar attributen
Idempotency-Key jou jouw naam voor één poging, zodat een herhaling één keer wordt beantwoord (Idempotentiesleutels)
If-Match jou de versie die je las, bij een schrijfactie die een policy vervangt (een PATCH /keys/{id}, een PUT of DELETE van de policy van een organisatie) of het werkgeheugen van een gesprek; een opgeslagen versie die sindsdien veranderde, is 409 met reason_code: "stale_version"
X-Core-Thread-Id jou en Core op /messages, het gesprek waar een opgeslagen beurt bij hoort (Een gesprek voortzetten op /messages)
X-Core-Previous-Response-Id jou en Core op /messages, het antwoord dat deze beurt voortzet
X-Core-Check Core bij een beurt die een live check tegenhield, welke check en waarom, zoals stage=input, check="geen-wachtwoorden", code=input_check_failed (Wat je applicatie ontvangt)
X-Core-Response-Id Core bij een mislukte beurt op /messages, het id van het antwoord dat hij achterliet, voor GET /responses/{id}
X-Request-Id Core het id van dit verzoek (hieronder)

Een tweede soort bearer. Naast je sleutel (ack_...) neemt Core in Authorization ook een subjecttoken (ast_...) aan: je sleutel beperkt tot één persoon voor hoogstens een uur, die je backend aanvraagt en doorgeeft aan een browser of apparaat dat de sleutel nooit mag hebben (Een token voor de browser).

De routes waarmee je beheert -- sleutels, prompts, skills, kennis -- volgen de afspraken van Core zelf: lijsten met een cursor, 204 bij verwijderen, 201 met Location bij opslaan. Die staan hieronder.

Request-id’s

Core voegt X-Request-Id: req_... toe aan antwoorden, ook bij fouten. Core maakt dit id zelf aan en neemt geen door de aanroeper gekozen waarde over. Bewaar het in je logs en vermeld het bij probleemmeldingen. Hetzelfde id staat in het foutantwoord en de bijbehorende registraties in Core. Fouten die ontstaan voordat het verzoek Core bereikt, kunnen een request-id missen.

Foutformaat

HTTP-fouten gebruiken het formaat application/problem+json, op elke route behalve de vijf modelroutes die hieronder staan:

{
  "type": "about:blank",
  "title": "Not Found",
  "status": 404,
  "detail": "The prompt was not found.",
  "request_id": "req_5Tn8..."
}

type zegt welke soort fout dit is. Publiceert je omgeving deze handleiding, dan is het het adres van die soort in de foutcatalogus, zoals https://docs.example.nl/en/reference/errors/#not-found; anders is het about:blank. De catalogus somt elke soort op, met wat je eraan doet.

Validatiefouten (422) bevatten ook een lijst errors:

{
  "status": 422,
  "errors": [{ "field": "tools.0.type", "message": "This tool type is not available for this key.", "type": "value_error" }]
}

Elke vermelding wijst een afgekeurd veld aan. Begint field met body., dan kon Core het verzoek niet verwerken, bijvoorbeeld door een ontbrekend veld of een verkeerd type. Een veld zonder dit voorvoegsel, zoals collection_ids of config.nodes[2].type, betekent dat het verzoek leesbaar was maar Core de waarde heeft afgekeurd.

De modelroutes antwoorden met de fout van de leverancier

POST /responses, GET /responses/{id}, POST /responses/{id}/tool_outputs, POST /responses/{id}/cancel en POST /messages geven fouten in de vorm die de clientbibliotheek leest van de API waarmee ze compatibel zijn. Een SDK van OpenAI of Anthropic die je op Core richt, werpt dus dezelfde uitzondering als bij de leverancier zelf, met jouw foutmelding en het betrokken veld erin.

De vier /responses-routes antwoorden met het foutobject van OpenAI:

{
  "error": {
    "message": "The agent fixes this field; send no value of your own, or the same one.",
    "type": "invalid_request_error",
    "param": "temperature",
    "code": "invalid_request"
  }
}

param noemt het veld waar de weigering over gaat, zonder het voorvoegsel body. dat de lijst errors van Core zelf gebruikt. code is de eigen code van Core voor die weigering, bijvoorbeeld invalid_request, policy_denied of usage_cap_exceeded.

POST /messages antwoordt met het foutobject van Anthropic. Dat heeft geen veld voor een parameter, dus de naam van het veld staat vooraan in de melding:

{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "temperature: The agent fixes this field; send no value of your own, or the same one."
  },
  "request_id": "req_5Tn8..."
}

Deze vijf routes antwoorden nooit 422. Een verzoek dat ze weigeren -- een veld dat ze niet kunnen inwilligen, een veld dat ontbreekt, een waarde die het veld niet toelaat -- is 400, de status waarmee beide leveranciers een body afkeuren. Elke andere route van Core houdt 422 met de lijst errors. Welke vorm je leest, volgt dus uit de route die je aanriep en niet uit wat er misging.

De statussen zelf staan in de tabel hieronder, en WWW-Authenticate en Retry-After gaan op dezelfde manier mee. Een ontbrekende of ongeldige sleutel weigert Core op deze routes zelf, dus die 401 komt ook in de vorm van de leverancier binnen, met WWW-Authenticate: Bearer.

Enkele antwoorden komen op deze vijf routes nog steeds als problem+json binnen. Een onverwachte 500 is er één. De andere komen van de gateway vóór Core, nog voordat Core het verzoek ziet: de eigen snelheidslimiet van de gateway (429) en zijn tijdslimiet (408) op alle vijf, en een body groter dan 1 MiB (413) op POST /responses/{id}/cancel, dat geen body van die omvang nodig heeft. De antwoorden van de gateway dragen de titel van hun status en geen X-Request-Id; daaraan herken je ze tegenover de antwoorden van Core zelf. Op de drie routes die een gesprek meedragen -- POST /responses, POST /messages en POST /responses/{id}/tool_outputs -- weigert Core een te grote body zelf, met 413 in de vorm van de leverancier: ze nemen tot 8 MiB aan, tenzij jouw installatie een andere grens heeft ingesteld, en de melding noemt die grens. Een body boven 1 MiB wordt alleen gelezen voor een geldige sleutel; met een ontbrekende of onjuiste sleutel volgt 401 zonder dat de body wordt gelezen. POST /messages antwoordt daarnaast 500 in de envelop van Anthropic, met type: api_error, als een beurt mislukt bij het model, bij pseudonimisering of op gestructureerde uitvoer; alleen een 500 die niemand voorzag is problem+json.

HTTP-statuscodes

Status Betekenis Actie
400 Een modelroute heeft het verzoek geweigerd (hierboven). Lees error.message en het veld in error.param.
401 Authenticatie is mislukt. Controleer de secret van de API-sleutel.
403 Het beleid weigert toegang, de bewerking staat niet open voor jouw rol, of de organisatie of een organisatie erboven is opgeschort; een opschorting draagt reason_code: "organisation_suspended" Lees detail en los de beperking op.
404 De resource is niet beschikbaar of de capability is uitgeschakeld. Controleer GET /key en daarna het resource-id.
408 Het verzoek was na 300 seconden nog niet volledig aangekomen; dit antwoord komt van de gateway (tijdslimiet). Stuur een kleinere body, of verstuur hem over een snellere verbinding.
409 De bewerking past niet bij de huidige toestand, of je organisatie wordt nog ingericht of wordt verwijderd (The tenant is not ready.). Lees detail; controleer op dubbele namen of een ongeldige statusovergang. Verwijder je iets dat nog in gebruik is, dan staat erbij wat het gebruikt (hieronder). Is de organisatie nog niet klaar, probeer het dan later opnieuw.
413 Het verzoek of de upload is te groot. Verklein de inhoud.
429 Een maandlimiet van je is bereikt, je verstuurt te veel verzoeken, of je houdt al zoveel tegelijk open als mag (eventstreams, workflowruns of -triggers). Wacht zo lang als Retry-After zegt als die er is, en herhaal anders met oplopende wachttijden. Een limiet antwoordt met een X-Request-Id-header en een detail dat vertelt welke van de twee limieten het was; de snelheidslimiet aan de rand heeft geen van beide. Zie je maandlimiet.
422 De validatie is mislukt op een andere route dan de vijf hierboven. Corrigeer de velden in errors.
500 Er is een onverwachte fout opgetreden. Bewaar het request-id en meld aanhoudende fouten.
503 Een benodigde dienst is niet beschikbaar. Probeer het opnieuw met steeds langere wachttijden.

De foutcatalogus splitst deze statussen in de soorten die type noemt: een 429 door een maandlimiet en een door de snelheidslimiet zijn bijvoorbeeld twee soorten.

Een 401 bevat WWW-Authenticate: Bearer. Een 403 betekent dat de authenticatie is geslaagd, maar de bewerking niet is toegestaan. Een 500 bevat nooit de interne fouttekst.

Iets verwijderen dat nog in gebruik is

Core verwijdert niets waar iets anders nog naar verwijst, en er wordt niets mee verwijderd. De DELETE antwoordt 409 en zegt wat het nog gebruikt, zodat je die verwijzingen kunt weghalen en het verwijderen opnieuw kunt sturen:

{
  "title": "Conflict",
  "status": 409,
  "detail": "The skill is named by an agent; remove it from the agent first.",
  "conflicts": [
    { "where": "agents", "id_or_name": "agent_7Kc2...", "label_or_condition": "helpdesk",
      "reason": "links skill skill_3Rt9...", "reason_code": "links_skill", "count": null }
  ],
  "withheld": [{ "where": "agents", "count": 2 }]
}
Veld Wat het zegt
conflicts[].where het soort ding dat het gebruikt, zoals agents, indexes, graphs, keys of retrieval tools
conflicts[].id_or_name, label_or_condition zijn id en zijn naam; bij een graaftype in beide de naam van het type
conflicts[].reason_code waarom het in de weg zit, uit een vaste lijst waar je client op kan schakelen: names_tool, names_toolset, delegates_to_agent, links_skill, selects_collection, ingests_with_pipeline, runs_pipeline, fills_graph, injects_memory, extraction_on, has_entities, has_relations, names_routing_config, default_routing_config, outside_vocabulary. De laatste hoort bij de 422 van een vocabulaire dat opgeslagen rijen tegenspreken, nooit bij een verwijdering. reason zegt hetzelfde in een zin voor mensen, en die formulering kan veranderen
conflicts[].count een getal waar een getal bedoeld is: hoeveel entiteiten of relaties van een graaftype een nieuw schema zou laten vallen. Anders null
withheld wat het ding gebruikt en wat je sleutel niet mag lezen, geteld per soort (agents, indexes, graphs, keys), zonder id of naam

conflicts bevat hoogstens de eerste twintig; zijn er meer, dan eindigt detail met More are not listed. Het verwijderen van een routeringsconfiguratie is de uitzondering: dat noemt tot honderd sleutels die haar nog noemen, en detail zegt daar niet of er meer zijn. Een agent, een index of een graaf die het beleid van je sleutel hem niet laat lezen, of een sleutel als de jouwe de capability management mist, wordt nooit genoemd: het wordt geteld in withheld, en een telling daar is exact zolang niet meer dan honderd dingen van die soort het ding gebruiken. Een zoektool wordt altijd genoemd. Een verwijdering kan dus 409 antwoorden met een lege conflicts; vraag iemand die die dingen wel kan lezen om de verwijzing weg te halen.

Dit geldt voor een skill die een agent koppelt, een agent die een andere agent als tool aanroept, een toolset, een connector, een MCP-server, een zoektool of een alias die een agent noemt, een collectie die een index selecteert, een pipeline die een index, een graaf of een zoektool gebruikt, een index waaruit een sleutel geheugen inspuit of waarvoor geheugenextractie aanstaat, en een graafschema dat een type weglaat waar de graaf nog feiten van bevat. Alleen de nieuwste versie van een agent telt.

Tijdslimiet voor een verzoek

De gateway geeft elk verzoek 300 seconden om volledig aan te komen, body inbegrepen, en antwoordt 408 als dat niet lukt. Alleen een grote body over een trage verbinding komt in de buurt van die grens. Een modelaanroep (POST /responses, POST /messages, POST /responses/{id}/tool_outputs) neemt standaard tot 8 MiB aan, en daarvoor is een uploadsnelheid van ongeveer 0,2 Mbit/s nodig om op tijd aan te komen; een upload naar POST /files neemt standaard tot 25 MiB aan, en daarvoor is ongeveer 0,7 Mbit/s nodig. Een nieuwe poging loopt tegen dezelfde grens aan, tenzij de body kleiner is of de verbinding sneller.

Limieten op vrije tekst

Een veld dat vrije tekst aanneemt heeft een limiet, en een verzoek daarboven wordt geweigerd met 422, met de naam van het veld en de limiet in de melding:

Veld Limiet
instructions van een agent, en template van een promptversie 262.144 tekens
Een waarde in de config van een abonnement 16.384 tekens
metadata van een collectie of een bron 64 paren, sleutels van hoogstens 128 en waarden van hoogstens 2.048 tekens
Het request van een schedule 256 KiB, geserialiseerd
tool_variables van een verzoek (aanroepvariabelen) 64 namen, een string van hoogstens 4.096 tekens (8.192 voor een geheim), 64 KiB in totaal; 400 op POST /responses en POST /messages
query van een zoekopdracht 4.096 tekens

Ook de instructions en de input van een beurt hebben een limiet, die op POST /responses met 400 wordt geweigerd: Limieten. Op POST /messages gelden dezelfde 262.144 tekens voor system; een langere wordt geweigerd met 400, met system als veld.

Paginering met cursors

Lijst-endpoints geven items terug en een cursor voor de volgende pagina:

{ "items": [...], "next_cursor": "eyJ2IjoxLCJr..." }

Stuur next_cursor mee als queryparameter cursor. Met limit stel je het aantal items per pagina in. Als next_cursor null is, heb je het einde bereikt.

curl -s "$CORE_URL/threads?limit=50" -H "Authorization: Bearer $CORE_KEY"
curl -s "$CORE_URL/threads?limit=50&cursor=eyJ2IjoxLCJr..." -H "Authorization: Bearer $CORE_KEY"

Gebruik de cursor ongewijzigd. Het formaat heeft een versie en kan veranderen. Paginering met een offset wordt niet ondersteund. Cursors voorkomen dat paginagrenzen verschuiven wanneer er nieuwe records worden toegevoegd.

Een lijst sorteren

Een lijst die te sorteren is, kent sort, de naam van een kolom, en order: asc of desc. De kolommen die een lijst aanbiedt zijn name, created_at en updated_at, voor zover de resource ze heeft; de API-referentie noemt ze per route. Een kolom die de route niet aanbiedt geeft een 422 met query.sort. Met sort zonder order loopt de lijst oplopend. Met geen van beide houdt hij zijn gebruikelijke volgorde: nieuwste eerst bij schedules en evaluations, oudste eerst bij de rest; alleen order leest die lijst in de richting die je noemt. De versielijsten van agents en prompts kennen order ook, zonder sort: ze lopen oudste eerst, en order=desc leest ze nieuwste eerst. De versielijsten van toolsets, tool-aliassen, MCP-servers en API-, mail- en SQL-connectors lopen nieuwste eerst, en order=asc leest ze oudste eerst.

curl -s "$CORE_URL/skills?sort=name&order=asc&limit=50" -H "Authorization: Bearer $CORE_KEY"

Rijen met dezelfde waarde in de gesorteerde kolom komen in een vaste volgorde, zodat er van de ene pagina naar de andere geen rij wordt overgeslagen of dubbel getoond. Namen worden vergeleken zoals de database tekst vergelijkt, dus hoofdlettergevoelig. Een cursor geldt alleen voor de sort en order waarmee hij is uitgegeven: stuur je hem met een andere, dan volgt een 422 met query.cursor. Begin dus opnieuw zonder cursor als je een van beide wijzigt.

Idempotentiesleutels

Stuur een header Idempotency-Key mee met POST /responses en gebruik dezelfde waarde bij een herhaling. Zodra Core het oorspronkelijke resultaat heeft vastgelegd, kan het de herhaling herkennen:

Oorspronkelijk antwoord Herhaling met dezelfde sleutel
Opgeslagen (201) 200 met het opgeslagen antwoord; geen extra modelaanroep
Niet opgeslagen (200) 409; er is geen opgeslagen antwoord om terug te geven

Laat opslag ingeschakeld als je het antwoord na een verbindingsfout wilt kunnen ophalen. Core kan een afgeronde aanroep zonder opslag herkennen, maar de uitvoer niet teruggeven.

Stuur geen gelijktijdige verzoeken met dezelfde sleutel. Als een herhaling aankomt voordat de eerste aanroep is vastgelegd, kunnen beide verzoeken het model aanroepen en verbruik opleveren. De conflicterende schrijfactie geeft 409. Idempotentie garandeert dus niet precies één aanroep naar de modelleverancier.

Verzoeken herhalen

Gebruik voor modelaanroepen eerst de hierboven beschreven idempotentie:

Situatie Afhandeling
503 of een verbindingsfout zonder antwoord Probeer het opnieuw met steeds langere wachttijden.
500 Probeer het opnieuw met langere wachttijden; meld het request-id als de fout blijft optreden.
Een weigering door de snelheidslimiet vóór Core, zonder X-Request-Id Wacht voordat je opnieuw probeert. Elke credential heeft een eigen limiet.
Andere 4xx Corrigeer het verzoek of de rechten voordat je opnieuw probeert.
Antwoord met status: failed Bekijk de fout in het antwoord. Een nieuwe modelpoging is een nieuwe beurt en kan extra verbruik opleveren.

Core beperkt het aantal herhalingen naar de modelleverancier. Een aanroep die een time-out geeft, kan al uitvoer hebben opgeleverd waarvoor kosten worden gerekend.

Herkomst van resources

Skills, prompts en hun versies, tools, MCP-servers, bestanden, kennisbronnen, indexen, pipelines en retrieval-tools bevatten created_by. Daarin staat het id van de sleutel die de resource aanmaakte, of operator: gevolgd door de identiteit van de consolegebruiker.

Gebruik dit veld voor herkomstinformatie en sortering. De organisatie is eigenaar; created_by verleent of beperkt geen toegang. Andere sleutels van de organisatie kunnen de resources gebruiken voor zover hun capabilities en beleid dat toestaan.

API-referentie

De API-referentie wijst je de twee verkenners van je installatie: ReDoc om elke route met zijn verzoek- en antwoordvorm te lezen, Swagger UI om er een uit te proberen met je sleutel. In het deel Basis-URL staat het adres waarop je elke route aanroept.