Laatst bijgewerkt: 23 september 2026
CustomerGate 365: naslag webservices
Voor ontwikkelaars die een website op CustomerGate 365 bouwen. Deze pagina beschrijft het aanmelden, de vorm van de endpoints, elke service en procedure, de JSON-sleutels en vaste waarden, de webhooks die Business Central naar u toe stuurt, en de beveiligingsregels erachter. Ze beschrijft appversie 1.0.42.0, de versie op AppSource. Sleutels en waarden met Sinds 1.0.44 komen met de volgende versie; een website die op 1.0.42.0 is gebouwd, blijft zonder die toevoegingen werken. Namen zijn hoofdlettergevoelig, en de AL-broncode van de app is doorslaggevend.
De services
CustomerGate 365 publiceert elf webservices: acht eigen en drie van iFacto. De app publiceert ze zelf bij de installatie en opnieuw bij elke update: een ontbrekende service wordt aangemaakt, en een service die onder die naam al bestaat, blijft altijd ongewijzigd, ook als iemand de publicatie met de hand heeft ingetrokken of de service heeft aangepast. De drie services van iFacto bestaan alleen waar Steel 365 is geïnstalleerd.
| Service | Soort | Waarvoor | Aangeroepen door |
|---|---|---|---|
voaItems | pagina, alleen lezen | Artikelen voor de catalogus | synchronisatietoepassing |
voaVariants | pagina, alleen lezen | Artikelvarianten (afmetingen) | synchronisatietoepassing |
voaCategories | pagina, alleen lezen | Artikelcategorieën | synchronisatietoepassing |
voaRemovals | pagina, alleen lezen | Wat uit de catalogus verdwenen is en dus van de website af moet | synchronisatietoepassing |
voaCatalogMedia | codeunit, unbound action | De bytes van één artikel- of variantfoto | synchronisatietoepassing |
voaProfiles | pagina (iFacto DXSteel) | Staalprofielen | synchronisatietoepassing |
voaGrades | pagina (iFacto DXSteel) | Staalkwaliteiten | synchronisatietoepassing |
voaItemTypes | pagina (iFacto DXSteel) | Staalartikelsoorten | synchronisatietoepassing |
voaRequests | codeunit, unbound actions | Een offerte- of accountaanvraag ontvangen en er een bestand aan toevoegen | websitetoepassing |
voaPortal | codeunit, unbound actions | Aanmelden en alles wat een aangemelde klant ziet of doet | websitetoepassing |
voaPing | codeunit, unbound action | Wat voor installatie dit is: versie, omgeving, modules, documentbron. Zonder sessie en zonder persoonsgegevens | websitetoepassing |
De websitetoepassing mag voaRequests, voaPortal en voaPing aanroepen, de synchronisatietoepassing de cataloguspagina's, voaRemovals en voaCatalogMedia. Geen van beide kan de services van de andere gebruiken: de websitetoepassing heeft geen uitvoerrecht op de catalogus, en de synchronisatietoepassing heeft er geen op het portaal of de aanvragen. Welke toepassing welke is, staat in de volgende paragraaf.
Aanmelden
De website communiceert met Business Central als twee Microsoft Entra-toepassingen. Hun client-id's vult u in op CustomerGate-instellingen; de actie Machtigingen herstellen registreert ze in Business Central en wijst hun machtigingensets toe. Voor die actie is SUPER nodig.
| Veld in de instellingen | Waarvoor | Mag aanroepen |
|---|---|---|
| Client-id website | Offerte- en accountaanvragen, het klantenportaal, de ping | voaRequests, voaPortal, voaPing |
| Client-id synchronisatie | De synchronisatie van de catalogus. Laat leeg als de catalogus niet wordt gesynchroniseerd. | voaItems, voaVariants, voaCategories, voaRemovals, voaCatalogMedia, voaProfiles, voaGrades, voaItemTypes |
Geen van beide toepassingen kan de services van de andere gebruiken: de websitetoepassing heeft geen uitvoerrecht op de cataloguspagina's, en de synchronisatietoepassing heeft er geen op voaPortal, voaRequests of voaPing.
Machtigingensets
| Set | Toegewezen aan | Wat de set toestaat |
|---|---|---|
VOA WEB API | website, alle bedrijven | voaRequests uitvoeren. Alleen indirect invoegen in de aanvraagtabellen, geen leesrecht. |
VOA WEB PORTAL | website, alle bedrijven | voaPortal en de codeunits die hij aanroept uitvoeren. Alleen indirecte tabelrechten. |
VOA WEB CONTRACT | website, alle bedrijven | voaPing en de codeunits die hij aanroept uitvoeren (sinds 1.0.44 ook de bijlageregels die hij onder attachments meldt), en verder niets. Een eigen set, omdat de twee sets hierboven elk precies hun eigen aanroepketen afdekken en voaPing in geen van beide zit. |
VOA WEB BASELINE | website, alle bedrijven | Het minimum dat een webservicesessie nodig heeft, in plaats van D365 BASIC, dat klant- en verkoopgegevens zou prijsgeven. |
VOA CG EXECUTE | website, tenant-set | Uitvoerrechten op de objecten die het mail-, schrijf- en pdf-pad bereiken. |
VOA CG MAIL | website, tenant-set | Wat de e-mailconnectors nodig hebben om mail te versturen, zoals de aanmeldcode. |
VOA CG S365 ARCHIVE | website, alleen met DXSteel Doc Archive | De archieftabellen lezen, met een beveiligingsfilter op de toegestane archiefrubrieken. |
VOA CG DOCUMENTS | website, alleen zonder dat archief en met Portaaldocumenten uit rapporten aan | Lezen wat de standaardverkooprapporten lezen, en die rapporten uitvoeren, zodat Document pdf's kan afdrukken. |
CDC BASIC | website, alleen met Continia Document Capture | Continia haakt in op de triggers van de tabellen waarin de website schrijft. |
VOA WEB SYNC | synchronisatie, per bedrijf | De vier eigen cataloguspagina's met hun brontabellen en de fotoservice, plus leesrecht op artikelposten voor de restvoorraad en op de mediatabel voor de foto's. |
VOA CG S365 SYNC | synchronisatie, alleen met Steel 365 | De drie DXSteel-pagina's en de tabellen erachter. |
Een token ophalen
Beide toepassingen gebruiken de OAuth 2.0-clientreferentiestroom:
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
grant_type=client_credentials
client_id={client id}
client_secret={secret}
scope=https://api.businesscentral.dynamics.com/.defaultStuur het toegangstoken bij elke aanroep mee als Authorization: Bearer {token}.
Endpoints en afspraken
Alle services zijn standaard OData v4-endpoints van Business Central onder https://api.businesscentral.dynamics.com/v2.0/{tenant}/{environment}/ODataV4.
Paginaservices
De cataloguspagina's leest u met GET. De referentiewebsite geeft het bedrijf aan met zijn naam:
GET .../ODataV4/Company('{company name}')/voaItems?$select=No,Description&$filter=...In een bedrijfsnaam verdubbelt u een apostrof (O''Brien), zoals OData voorschrijft. Grote resultaten komen in pagina's: volg @odata.nextLink tot die ontbreekt. De records staan in value.
Codeunitservices
voaRequests, voaPortal, voaPing en voaCatalogMedia bieden elke openbare procedure aan als unbound action met de naam {service}_{Procedure}. Roep die aan met POST en geef de parameters op naam mee in een JSON-body. De referentiewebsite geeft het bedrijf aan met zijn GUID, de id van het bedrijf in de standaard companies-API:
POST https://api.businesscentral.dynamics.com/v2.0/{tenant}/{environment}/ODataV4/voaPortal_ReadSession?company={company id}
Authorization: Bearer {access token}
Content-Type: application/json
Accept: application/json
Accept-Language: en-US
{"token": "<session token>"}De retourwaarde staat in de OData-envelop onder value:
{"@odata.context": "…", "value": "{\"email\":\"buyer@example.com\",\"customerNo\":\"C00010\", …}"}Afspraken
- Namen zijn hoofdlettergevoelig en de koppeling gaat op naam.
voaPortal_readSessionbestaat niet, en een parameter dieemailAddressheet in plaats vanemail, levert een 400 op zonder verdere uitleg. - Elke parameter is een tekenreeks: alle procedureparameters zijn AL-
Text. Ook een getal alspictureIndexstuurt u als"1". - Een JSON-resultaat komt terug als tekenreeks binnen
value, omdat een unbound action geen genest object kan teruggeven. Ontleedvalueeen tweede keer. Een booleaanse uitkomst is een JSON-trueoffalseinvalue. - Een lege tekenreeks is een antwoord. Bij de meeste procedures van
voaPortalbetekent"": geen geldige sessie, of hier is niets voor u. - Optionele sleutels worden weggelaten, niet als null verstuurd. Behandel een ontbrekende sleutel als null.
- Datums zijn
YYYY-MM-DD, tijdstempels zijn ISO 8601 in UTC (2026-09-19T14:30:00.000Z). Een lege datum of tijdstempel is"". - Getallen en booleans zijn JSON-getallen en JSON-booleans.
Accept-Languagebepaalt de taal van de Business Central-sessie. Dat heeft invloed op de foutmeldingen en de optiebijschriften in de catalogus, nooit op de vaste waarden verderop. De referentiewebsite stuurtnl-NL.
Statuscodes en opnieuw proberen
| Status | Wat het hier betekent |
|---|---|
| 200 | De procedure is uitgevoerd. De uitkomst staat in value. |
| 400 | Er trad een AL-fout op, bijvoorbeeld bij een geweigerde aanvraag (de melding staat in de body, met een merkteken [VOA-…] ervoor), of de parameternamen kloppen niet. |
| 401 / 403 | Token of machtigingensets kloppen niet. De body noemt het object dat ontbreekt. |
| 404 | De service is niet onder die naam gepubliceerd, of het bedrijf klopt niet. |
| 409 | Gelijktijdige aanroepen met hetzelfde sessietoken probeerden dezelfde sessierij bij te werken. |
| 429 | Business Central remt de aanroepen af. Er is niets verwerkt; houd u aan Retry-After. |
| 5xx | Serverfout. Bij een aanroep die iets wijzigt, weet u niet of hij is uitgevoerd. |
Na een 429 kunt u altijd veilig opnieuw proberen. Na een netwerkfout, een time-out, een 409 of een 5xx probeert u alleen de lezende procedures opnieuw: ReadSession, MyDetails, MyDeliveryAddresses, MyRequests, RequestLines, MyQuotes, MyOrders, MyShipments, DocumentLines, MyDocuments, Document, MyAttachments, Attachment, MyNotificationSettings, Ping en de cataloguspagina's.
Probeer RequestCode, SignIn, SignOut, LinkRequest, AcceptQuote, AcceptQuoteWithDetails, RejectQuote, SetNotificationSettings en InviteColleague niet op eigen initiatief opnieuw. SwitchCustomer is veilig met dezelfde customerNo: wisselen naar de klant waarop de sessie al staat, verandert niets en geeft switched terug. Bij CreateQuoteRequest en CreateAccountRequest is opnieuw proberen alleen veilig met dezelfde requestId. AddAttachment is altijd veilig met dezelfde bytes: dezelfde inhoud bij dezelfde aanvraag blijft één bijlage.
Het beveiligingsmodel
Er zijn twee lagen. De Entra-toepassing bewijst dat de aanroeper de website is; ze identificeert nooit een klant. Het sessietoken van het portaal identificeert de aangemelde persoon, en Business Central leidt daar de klant uit af. Het klantnummer gaat niet van de website naar Business Central: alleen SwitchCustomer accepteert er een, en die wisselt alleen tussen klanten die aan het aangemelde adres gekoppeld zijn. Geen enkele andere procedure accepteert een klantnummer, een e-mailfilter of een andere parameter die bepaalt wiens gegevens er terugkomen.
Portaalgebruikers
- Iemand mag het portaal gebruiken zodra de binnendienst zijn accountaanvraag heeft goedgekeurd. Daarmee ontstaat een portaalgebruiker: een e-mailadres gekoppeld aan één klant, met een rol. De website kan geen portaalgebruikers aanmaken.
- E-mailadressen worden in kleine letters vergeleken, zonder spaties eromheen.
- Eén adres mag aan meerdere klanten gekoppeld zijn. Een portaalgebruiker is het paar (e-mailadres, klant), dus dezelfde persoon kan bij twee bedrijven een koppeling hebben, elk met een eigen rol. Bij het aanmelden krijgt de sessie het laagste klantnummer dat tot het portaal is toegelaten;
ReadSessionenMyDetailsnoemen alle klanten waarvoor het adres mag optreden, enSwitchCustomerzet de sessie over naar een andere klant. - De portaalgebruiker moet actief zijn en mag niet geanonimiseerd zijn; de klant moet bestaan, mag niet vanwege privacy geblokkeerd zijn en mag niet op Alle geblokkeerd staan. Klanten die alleen op Verzenden of Factuur geblokkeerd staan, houden hun toegang.
Aanmelden met een eenmalige code
- De bezoeker vult zijn e-mailadres in; de website roept
RequestCode(email, ipHash)aan. - Business Central maakt een code, bewaart daar alleen een gezouten hash van en mailt de code naar het adres als het bij een actieve portaalgebruiker hoort.
- De bezoeker vult de code in; de website roept
SignIn(email, code, ipHash)aan en krijgt een sessietoken, of"". - De website bewaart het token, bijvoorbeeld in een HttpOnly-cookie, en geeft het als
tokenmee aan elke andere procedure vanvoaPortal. SignOut(token)beëindigt de sessie.
De code bestaat uit tien tekens uit het alfabet 0123456789ABCDEFGHJKMNPQRSTVWXYZ (geen I, L, O of U) en staat in de e-mail als twee groepen van vijf met een koppelteken ertussen. Voor de vergelijking wordt hij genormaliseerd: spaties en koppeltekens eruit, alles in hoofdletters, O gelezen als 0, I en L als 1. Pas dezelfde regels toe als u de invoer op de website controleert.
- Een code blijft geldig zolang Geldigheid aanmeldcode (min.) aangeeft, standaard 15 minuten, en werkt maar één keer. Na een geslaagde aanmelding verlopen alle andere open codes voor dat adres.
- Verkeerd raden sluit niemand buiten. Mislukte pogingen worden geteld bij de nieuwste open code, tot Maximum codepogingen, als spoor om misbruik te herkennen.
- Er kunnen meerdere codes tegelijk geldig zijn: een nieuwe code maakt eerdere niet ongeldig.
RequestCodeantwoordt altijdtrue, ongeacht of het adres bekend, gekoppeld of geblokkeerd is en of de mail is mislukt, en duurt altijd minstens 1,5 seconde. Zo verklapt het endpoint aan niemand welke adressen van klanten zijn.- Tussen twee codes voor hetzelfde adres geldt een minimale wachttijd van Wachttijd aanmeldcode (sec.), standaard 60 seconden. Een aanvraag binnen die tijd krijgt
true, maar er gebeurt niets. - De mail met de code vertrekt direct tijdens de aanroep van
RequestCode, niet via de taakwachtrij. Hij is in de taal van de accountaanvraag waaruit de portaalgebruiker is ontstaan, anders in de taal van de klant, anders in die van de binnendienst.
Sessietokens
- Een token bestaat uit 64 tekens: twee GUID's in hexadecimale hoofdletters. Behandel het als een ondoorzichtige waarde; Business Central bewaart alleen de SHA-256-hash ervan. Spaties voor en na
tokenworden genegeerd. - Schuivende vervaltijd: een sessie verloopt als ze Sessieduur (min.) lang niet is gebruikt, standaard 480. Elke geslaagde aanroep verlengt de sessie; dat wordt hooguit één keer per minuut weggeschreven, dus een sessie kan tot een minuut eerder eindigen dan de nominale tijd.
- Absolute grens: geen sessie blijft na het aanmelden langer bestaan dan Maximale sessieleeftijd (uren), standaard 168, hoe actief ze ook wordt gebruikt.
- Bij elke aanroep wordt gecontroleerd of het token bestaat, niet is ingetrokken, niet is verlopen en binnen de maximale leeftijd valt, of de portaalgebruiker nog bestaat en actief is, en of de klant nog tot het portaal is toegelaten. Mislukt de controle op de portaalgebruiker of de klant, dan wordt de sessie meteen ingetrokken.
- De rol wordt bij elke aanroep uit de portaalgebruiker gelezen, niet uit de sessie, dus een wijziging van Inkoper naar Meekijker geldt meteen.
ReadSessiongeeftexpiresAtterug. Gebruik dat voor de levensduur van de cookie.- Als meerdere aanroepen tegelijk hetzelfde token gebruiken, kan Business Central op een ervan met 409 antwoorden. De lezende procedures kunt u veilig één keer opnieuw proberen.
Het klantslot
Alles wat voaPortal teruggeeft, is gefilterd op de klant van de sessie, en geen enkele parameter kan dat filter weghalen:
| Gegevens | Gefilterd op |
|---|---|
| Webaanvragen met hun regels en bijlagen | Klantnr. van de aanvraag |
| Verkoopoffertes en -orders, actueel en gearchiveerd | Orderklantnr. |
| Geboekte verzendingen, facturen, creditnota's | Orderklantnr. |
| Leveradressen | Klantnr. |
| DXSteel Doc Archive | de relatie is deze klant, het relatietype is Klant, en de rubriek is toegestaan |
Een documentnummer is een parameter in DocumentLines, Document, AcceptQuote, AcceptQuoteWithDetails en RejectQuote, en een aanvraagnummer in RequestLines en MyAttachments. Het zegt wat er wordt gevraagd, nooit van wie het is. Documentnummers worden getrimd en omgezet naar hoofdletters; een nummer langer dan 20 tekens geldt als niet gevonden, niet als afgekapt.
Eén adres bij meerdere klanten
Een inkoper die voor twee bedrijven van dezelfde groep inkoopt, is in de handel heel gewoon. Zo iemand heeft twee koppelingen, elk met een eigen rol, en meldt zich één keer aan.
| Wat | Waar |
|---|---|
| Voor welke klanten deze sessie mag optreden | customers in ReadSession en MyDetails |
| Welke klant nu actief is | customerNo in ReadSession |
| De sessie naar een andere klant overzetten | SwitchCustomer |
| Een koppeling maken of intrekken | De binnendienst, in de Business Central-client. Er is geen webservice voor. |
De actieve klant staat op de sessierij in Business Central, niet in een cookie. Daarop rust de hele beveiliging van deze functie: de website kan niet beslissen wiens gegevens ze ziet, ze kan alleen vragen om te wisselen naar een klant waarvan Business Central kan bevestigen dat hij aan het aangemelde adres gekoppeld is. Wisselen naar een ander klantnummer levert refused op, en er verandert niets.
SwitchCustomer is de enige procedure in het portaal die een klantnummer accepteert, en daarom zijn de regels eromheen strenger dan elders:
- de koppeling (adres, klant) moet bestaan en actief zijn, en mag niet geanonimiseerd zijn;
- de klant moet tot het portaal zijn toegelaten, met dezelfde controle als bij het aanmelden: niet vanwege privacy geblokkeerd en niet op Alle geblokkeerd;
- de rol van de nieuwe sessie komt uit de koppeling waarnaar is gewisseld, niet uit de sessie. Dezelfde persoon kan bij het ene bedrijf Inkoper zijn en bij het andere Meekijker;
- elke poging, geslaagd of geweigerd, wordt in Business Central in een logboek geschreven met het adres, beide klantnummers, de IP-hash en de reden. De website krijgt de reden nooit te horen.
Na een geweigerde wissel is de sessie onveranderd: ze leest nog steeds de gegevens van de klant waarop ze stond. Een half uitgevoerde wissel bestaat niet. En refused zegt nooit waarom, precies zoals overal in dit koppelvlak: met een reden zou dit endpoint een gratis manier worden om na te gaan welke klantnummers bestaan en wie eraan gekoppeld is.
Overal hetzelfde lege antwoord
Een document dat niet bestaat en een document van een andere klant geven precies hetzelfde antwoord. Anders zou een lijst nummers genoeg zijn om af te tasten wat er bestaat.
| Procedure | Geen geldige sessie | Niet gevonden, niet van u, niet toegestaan |
|---|---|---|
RequestCode | niet van toepassing | true (altijd) |
SignIn | niet van toepassing | "" (verkeerde, verlopen of gebruikte code, inactieve gebruiker) |
SignOut | true | true |
ReadSession, MyDetails, MyRequests, MyQuotes, MyOrders, MyShipments, MyDocuments | "" | niet van toepassing: een lijst mag leeg zijn |
MyDeliveryAddresses | "" | "" voor een Meekijker |
LinkRequest | false | false |
DocumentLines | "" | ""; een bestaand eigen document zonder artikelregels geeft {"lines":[]} |
RequestLines | "" | ""; een eigen aanvraag zonder regels geeft {"requestNo":…,"lines":[]} |
MyAttachments | "" | ""; een eigen aanvraag zonder bestanden geeft {"requestNo":…,"attachments":[]} |
Attachment | "" | "" (onbekende id, een id die niet van u is, of een bijlage zonder inhoud) |
Document | "" | "", ook als er geen rapport is ingesteld, of met het archief van Steel 365 |
AcceptQuote, AcceptQuoteWithDetails, RejectQuote | {"outcome":"refused",…} | {"outcome":"refused",…} |
SwitchCustomer | {"outcome":"refused",…} | {"outcome":"refused",…} |
MyNotificationSettings, SetNotificationSettings | "" | niet van toepassing |
InviteColleague | {"outcome":"refused",…} | {"outcome":"already"} bij een adres dat al toegang heeft; {"outcome":"throttled"} boven de daglimiet |
Rollen
| Rol | Waarde | Mag |
|---|---|---|
| Inkoper | Buyer | Alles wat een Meekijker mag, plus LinkRequest, MyDeliveryAddresses, AcceptQuote, AcceptQuoteWithDetails, RejectQuote en InviteColleague. |
| Meekijker | Viewer | Lezen: sessie, gegevens, aanvragen, aanvraagregels, offertes, orders, verzendingen, documentregels, documenten en bijlagen. Plus de eigen berichtvoorkeuren en SwitchCustomer. |
Een nieuwe portaalgebruiker is Meekijker, tenzij de binnendienst Inkoper kiest, en een onbekende rolwaarde wordt gemeld als Viewer. Bij een Meekijker antwoordt LinkRequest met false, MyDeliveryAddresses met "" en AcceptQuote, AcceptQuoteWithDetails, RejectQuote en InviteColleague met refused. De berichtvoorkeuren en SwitchCustomer staan voor beide rollen open: juist een Meekijker wil weten dat er een verzending is geboekt, en de rol van een sessie komt uit de koppeling waarnaar is gewisseld. Een Meekijker kan wel een offerteaanvraag versturen via voaRequests; die wordt dan niet aan de klant gekoppeld, en de binnendienst behandelt die als een aanvraag van een anonieme bezoeker. MyDetails geeft role terug, zodat het formulier dat kan melden.
IP-hash
Verschillende aanroepen krijgen een ipHash mee. Business Central krijgt het IP-adres van de bezoeker nooit te zien: de website berekent een hash met een sleutel. De referentiewebsite gebruikt HMAC-SHA256 met een geheime sleutel uit haar eigen configuratie, als 64 hexadecimale tekens in kleine letters. Business Central slaat de waarde ongewijzigd op, tot 64 tekens, bij de aanmeldcode, de sessie, het akkoord op en de afwijzing van een offerte, de klantwissel en de aanvragen. Het gebruikt de waarde alleen om misbruik te herkennen, en de opruimtaak wist haar na de bewaartermijn. Houd de sleutel geheim en ongewijzigd: zonder sleutel is een IPv4-hash terug te rekenen.
Portaalinstellingen
Per bedrijf, op de pagina CustomerGate-instellingen:
| Instelling | Standaard | Toegestaan | Effect |
|---|---|---|---|
| Geldigheid aanmeldcode (min.) | 15 | 1 t/m 1440 | Hoe lang een code geldig is. |
| Maximum codepogingen | 5 | 1 t/m 20 | Hoeveel mislukte pogingen per code worden geteld. Sluit niemand buiten. |
| Wachttijd aanmeldcode (sec.) | 60 | 0 t/m 3600 | Minimale tijd tussen twee codes voor één adres. 0 schakelt de wachttijd uit. |
| Sessieduur (min.) | 480 | 5 t/m 20160 | Schuivende vervaltijd. |
| Maximale sessieleeftijd (uren) | 168 | 1 t/m 480 | Het absolute einde van een sessie. |
| Portaaldocumenten uit rapporten | aan | Zie MyDocuments en Document. | |
| Portaalberichten ingeschakeld | uit | De hoofdschakelaar van het bedrijf voor de berichten uit MyNotificationSettings. Na een nieuwe installatie en na elke update staat hij uit, en uit betekent dat er niets in de wachtrij komt en niets wordt verstuurd, wat een portaalgebruiker ook heeft gekozen. | |
| Maximum aantal uitnodigingen per dag | 5 | 0 t/m 100 | Hoeveel collega's één Inkoper per dag mag uitnodigen met InviteColleague. 0 schakelt uitnodigen uit; er wordt dan niet teruggevallen op de standaardwaarde. |
| Aanvragen weigeren | uit | Laat CreateQuoteRequest en CreateAccountRequest mislukken met een fout. Heeft geen invloed op het portaal. |
Catalogusservices
De catalogusservices zijn alleen-lezen lijstpagina's, bedoeld voor een nachtelijke synchronisatie en niet voor live paginaweergaven. Gebruik $select om alleen de eigenschappen op te halen die u nodig hebt. De OData-eigenschapsnamen zijn de namen van de paginabesturingselementen hieronder en horen bij het contract. De waarden worden live uit de tabellen gelezen, ook de DXSteel-velden; die leest de app op veldnaam, zodat hij niet afhankelijk is van de DXSteel-app. Zonder Steel 365 geeft een DXSteel-tekstveld "" terug en een DXSteel-getal 0.
De feed bevat alleen wat op de website hoort
voaItems, voaVariants en voaCategories geven alleen gepubliceerde records terug. Welke artikelen dat zijn, wordt in Business Central beslist en niet door u: per artikel, per artikelcategorie, met publicatieregels in een vaste volgorde en een beleid op CustomerGate-instellingen. Een uitgesloten artikel zit helemaal niet in de feed, ook niet met een vlag of met een lege omschrijving. Het filter zit in de query en is met $filter niet weg te halen.
BlockedenSalesBlockedblijven invoaItemsstaan en zijn altijdfalse. Een geblokkeerd artikel wordt nooit gepubliceerd. De twee eigenschappen blijven bestaan voor compatibiliteit; haal uw afhandeling ervan niet weg, maar gebruik ze ook niet om artikelen van de website te weren.- De feed is leidend, maar een feed zegt niets over wat er niet in staat. Een artikel dat verdwijnt, komt niet meer langs, dus een upsert laat het gewoon staan. Daar is
voaRemovalsvoor: alles wat u hebt en wat niet in de feed zit maar wel invoaRemovalsverschijnt, moet weg. - Voor foto's geldt dezelfde regel: van een artikel dat niet in de feed zit, krijgt u ook geen foto.
Een bedrijf waarvoor de beslissing nog nooit is berekend, krijgt helemaal niets: de drie cataloguspagina's en voaCatalogMedia mislukken dan met [VOA-CatalogNotComputed] in plaats van een ongefilterde catalogus terug te geven. De app berekent de beslissing bij de installatie, bij het aanmaken van een bedrijf en bij elke update, dus dit hoort niet voor te komen. Gebeurt het toch, stop dan de synchronisatie en houd wat u hebt: een weigering is geen lege catalogus. De beheerder kiest Webcatalogus herberekenen op CustomerGate-instellingen.
voaItems
Brontabel: Artikel. Er worden geen prijzen, kosten of voorraadwaarden meegegeven.
| Eigenschap | Type | Bron |
|---|---|---|
No | string | Nr. |
Description | string | Omschrijving |
Description2 | string | Omschrijving 2 |
BaseUnitofMeasure | string | Basiseenheid |
DXPriceUOMSalesDMX | string | DXSteel Price UOM (Sales) DMX |
ItemCategoryCode | string | Artikelcategoriecode |
DXItemProfileCodeDMX | string | DXSteel Item Profile Code DMX |
DXQualityCodeDMX | string | DXSteel Quality Code DMX, de kwaliteit |
Blocked | boolean | Geblokkeerd. Altijd false: een geblokkeerd artikel wordt niet gepubliceerd. |
SalesBlocked | boolean | Verkoop geblokkeerd. Altijd false, om dezelfde reden. |
webQuotable | boolean | Niet online bestelbaar, omgekeerd. false betekent dat het artikel wel in de catalogus staat maar niet in een aanvraag mag: prijs op aanvraag, bel ons, of er is eerst een tekening nodig. Toon zo'n artikel zonder bestelknop; voaRequests weigert een regel met dit artikel. Let op de kleine w. |
pictureCount | number | Hoeveel foto's het artikel in Business Central heeft. |
pictureId | string | Een kenmerk van de huidige foto's: 64 hexadecimale tekens, of "" als het artikel geen foto heeft. Zie Foto's hieronder. |
voaVariants
Brontabel: Artikelvariant. Een variant volgt zijn artikel: varianten van een artikel dat niet gepubliceerd is, staan niet in deze feed.
| Eigenschap | Type | Bron |
|---|---|---|
ItemNo | string | Artikelnr. |
Code | string | Code |
Description | string | Omschrijving |
Description2 | string | Omschrijving 2 |
DXLength | number | DXSteel Length DMX |
DXWidth | number | DXSteel Width DMX |
DXThickness | number | DXSteel Thickness DMX |
DXExtraLength | number | DXSteel Extra Length DMX |
DXFormatType | string | DXSteel Format Type DMX, als bijschrift in de taal van de sessie. Stuur een vaste Accept-Language mee als u deze bijschriften omzet naar uw eigen model. |
Blocked | boolean | Geblokkeerd. Dit is de eigen vlag van de variant, en daar wordt niet op gefilterd: de website beslist. |
DXRemainingInventory | number | DXSteel Remaining Inventory DMX, opgeteld over alle locaties |
pictureCount | number | Hoeveel eigen foto's deze variant heeft. 0 betekent geen eigen foto, niet geen foto: val dan terug op die van het artikel. |
pictureId | string | Hetzelfde soort kenmerk als op voaItems, voor de eigen foto's van deze variant. |
voaCategories
Brontabel: Artikelcategorie, met de eigenschappen Code en Description. Er is geen veld voor de bovenliggende categorie; de referentiewebsite leidt de boom af uit de code (BALK-IPE hoort onder BALK). Een artikelcategorie die zelf of via een bovenliggende categorie is uitgesloten, staat niet in deze feed, zodat een uitgesloten tak geen leeg menu-item op de website achterlaat.
voaRemovals
Brontabel: Webcatalogus - verwijderingen. Alles wat uit de catalogus is verdwenen en niet is teruggekomen. Lees deze service in dezelfde ronde als de andere drie, anders blijft een artikel dat u in Business Central hebt uitgesloten voorgoed op de website staan.
| Eigenschap | Type | Wat het is |
|---|---|---|
Kind | string | Item, Variant of Category: een vaste waarde, nooit vertaald |
No | string | Het artikelnummer, of de code van de artikelcategorie |
VariantCode | string | De variant, bij Kind = Variant; anders "" |
RemovedAt | tijdstempel | Wanneer het record uit de catalogus verdween |
Verder niets: geen omschrijving, geen prijs, geen reden. Een uitgesloten artikel is juist iets dat de buitenwereld niet hoort te zien. Vraag alles op sinds uw vorige geslaagde ronde, verwijder die records of haal ze offline, en bewaar het tijdstip van de ronde. U hoeft geen bevestiging terug te sturen: het filter op RemovedAt is het hele protocol.
GET .../ODataV4/voaRemovals?$filter=RemovedAt gt 2026-09-19T02:00:00Z&$orderby=RemovedAt- Een rij verdwijnt zodra het record weer in de catalogus komt. Een record dat weer in de feed staat, gaat voor op een verwijderrij die u eerder hebt opgehaald, dus verwerk eerst de feed en daarna de verwijderingen, of controleer ze nog eens aan de hand van de feed.
- Er worden rijen geschreven voor een uitsluiting, voor een blokkering, voor een verwijderd artikel, een verwijderde variant of een verwijderde categorie, en voor het oude nummer na een nummerwijziging (het nieuwe nummer staat in de feed).
- Een
Variant-rij wordt alleen geschreven als de variant verdwijnt terwijl het artikel blijft. Verdwijnt het artikel, dan dekt zijn eigen rij de varianten: haal het artikel met alles eronder weg. - Rijen ouder dan 180 dagen worden opgeruimd. Een website die een half jaar niet heeft gesynchroniseerd, kan niet op deze lijst vertrouwen en moet haar hele catalogus één keer met de feed vergelijken.
Foto's
De foto's van een artikel staan in Business Central in een MediaSet, en OData kan hun bytes niet via een pagina-eigenschap doorgeven. De catalogus verdeelt het werk daarom: de feed zegt of er iets op te halen valt en of het is veranderd, en een actie geeft telkens één foto terug.
| Wat | Waar |
|---|---|
| Heeft dit artikel foto's, en zijn ze veranderd? | pictureCount en pictureId op voaItems |
| Heeft deze variant eigen foto's? | pictureCount en pictureId op voaVariants |
| De bytes van één foto | voaCatalogMedia_Picture |
pictureId is een kenmerk, geen naam. Het is een SHA-256-hash van de id's van de foto's in de set, op volgorde. De id van de MediaSet zelf is hiervoor niet bruikbaar: die identificeert de set en kan gelijk blijven terwijl de foto's erin worden vervangen, zodat een synchronisatie die erop vertrouwt, de nieuwe foto nooit zou ophalen. Vergelijk pictureId met wat u hebt bewaard; wijkt het af, haal de foto's dan opnieuw op. Is het "", dan heeft het artikel geen foto.
Een foto die in Business Central wordt verwijderd, komt binnen als een lege pictureId, in de volgende ronde van dezelfde feed die u toch al leest. Hij staat bewust niet in voaRemovals: die service gaat over records die uit de catalogus zijn verdwenen, en als daar ook wijzigingen op veldniveau in kwamen, zou u twee mechanismen voor hetzelfde op elkaar moeten afstemmen. Verwijder uw kopie als pictureId van gevuld naar leeg gaat, en als het artikel zelf in voaRemovals verschijnt.
voaCatalogMedia
voaCatalogMedia_Picture geeft één foto terug. De parameters zijn tekenreeksen, zoals overal:
| Parameter | Toelichting |
|---|---|
itemNo | Het artikelnummer. Wordt getrimd en omgezet naar hoofdletters. |
variantCode | De variantcode voor een variantfoto, of "" voor de foto van het artikel zelf. |
pictureIndex | "1" tot en met pictureCount, als tekenreeks. Een lege of onleesbare waarde telt als 1. |
| Sleutel | Type | Toelichting |
|---|---|---|
itemNo | string | |
variantCode | string | "" bij een artikelfoto. |
pictureIndex | number | Welke foto dit is. |
pictureCount | number | Hoeveel foto's er zijn, zodat u ze kunt doorlopen zonder terug te gaan naar de feed. |
pictureId | string | Hetzelfde kenmerk als in de feed; vergelijk het met de waarde waarop u het ophalen baseerde. |
contentType | string | image/jpeg, image/png enzovoort, zoals Business Central het heeft opgeslagen; application/octet-stream als Business Central het niet weet. |
fileName | string | {item}-{index}.{ext} of {item}-{variant}-{index}.{ext}, afgeleid van het inhoudstype. |
byteSize | number | De grootte van de foto in bytes, vóór base64. |
base64 | string | De bytes. Ontbreekt als de foto boven de limiet uitkomt of niet te lezen is. |
tooLarge | boolean | Alleen aanwezig, en dan true, als de foto boven de limiet uitkomt. |
unreadable | boolean | Alleen aanwezig, en dan true, als Business Central de foto helemaal niet kan laden. byteSize is dan 0. |
De service geeft "" terug, hetzelfde lege antwoord als overal, als het artikel niet bestaat, niet in de webcatalogus staat, de variant er niet bij hoort, of die foto er niet is. Voor een uitgesloten artikel komt er geen foto: anders zou een geraden artikelnummer alsnog de foto van iets interns opleveren. In een bedrijf waar de catalogus nooit is berekend, mislukt ook deze service met [VOA-CatalogNotComputed].
De limiet is Maximale fotogrootte (MB) op CustomerGate-instellingen, standaard 10 MB. Daarboven krijgt u het hele antwoord zonder base64 en met tooLarge: true. Dat is met opzet geen fout: één foto rechtstreeks uit een camera mag geen nachtronde afbreken. Een foto die Business Central niet kan laden, komt op dezelfde manier terug, met unreadable: true en zonder base64. Behandel beide gevallen hetzelfde: sla die ene foto over, meld hem en ga verder. Lees unreadable niet als "dit artikel heeft geen foto": pictureCount telt hem nog mee, en de foto in Business Central moet worden vervangen.
Een nachtronde met foto's
- Haal
voaItemsop metpictureIdenpictureCounterbij, naast de kolommen die u toch al synchroniseert. - Haal voor elk artikel waarvan
pictureIdafwijkt van wat u hebt bewaard, de foto's één voor één op metvoaCatalogMedia_Picture, van index 1 tot en metpictureCount, en bewaarpictureIdnaast de bestanden. IspictureIdleeg geworden, verwijder dan uw kopie. - Doe hetzelfde voor
voaVariants, metvariantCodegevuld. - Haal alleen op wat is veranderd. Een catalogus van tienduizend artikelen met foto's beslaat gigabytes; een nachtronde die ze allemaal ophaalt, raakt nooit klaar. Daar is
pictureIdvoor: de tweede nacht kost bijna niets. - Eén foto per aanroep. Er is geen batchverwerking: base64 maakt de payload een derde groter, en Business Central beantwoordt toch maar één webserviceaanroep tegelijk. Loop de lijst af en stop als uw tijdsbudget op is; dankzij de kenmerken gaat de volgende ronde verder waar u bent gestopt.
- Er wordt niets verkleind. U krijgt precies wat in Business Central staat, in de oorspronkelijke grootte en indeling. Maak zelf uw miniaturen en webformaten.
voaProfiles, voaGrades, voaItemTypes
Deze drie wijzen naar pagina's van iFacto Steel 365 (DXSteel), niet naar pagina's van deze app. Hun eigenschappen zijn van iFacto en kunnen met een update van iFacto veranderen; deze naslag legt ze niet vast. Ze bestaan alleen waar Steel 365 is geïnstalleerd, en de webcatalogus filtert ze niet: het zijn basisgegevens, geen artikelen. Stelt u er een filterlijst mee samen, baseer die dan op de artikelen die u werkelijk hebt ontvangen.
voaRequests: offerte- en accountaanvragen
Drie acties. Twee ervan krijgen de hele aanvraag als JSON in één tekstparameter payload en geven {"number":"…"} als tekst terug; de derde voegt een bestand toe aan een aanvraag en wordt beschreven onder Bijlagen hieronder. Het websiteaccount kan aanvragen aanmaken, maar niet teruglezen: er is geen service die aanvragen opsomt of een klant op e-mailadres opzoekt.
| Actie | Parameters | Geeft terug |
|---|---|---|
voaRequests_CreateQuoteRequest | payload | string: {"number":"WEB000123"} |
voaRequests_CreateAccountRequest | payload | string: {"number":"ACC000045"} |
voaRequests_AddAttachment | requestNo, fileName, contentBase64 | string: {"ok":true,"attachmentId":10000,…} |
Hoe de payload wordt gelezen: een ontbrekende sleutel en een JSON-null betekenen allebei leeg ("", 0 of false); tekstwaarden worden zonder melding afgekapt op de veldlengte; Code-velden worden in hoofdletters opgeslagen; de aanroep loopt in één transactie, dus als er iets mislukt, wordt er niets opgeslagen. Business Central vult zelf het aanvraagnummer, de status New, het tijdstip van ontvangst, de gekoppelde klant en bij accountaanvragen de beoordelaar en de reden van afwijzing in; de payload doet dat nooit.
CreateQuoteRequest: sleutels in de kop
| Sleutel | Type | Verplicht | Max | Toelichting |
|---|---|---|---|---|
requestId | string | aanbevolen | 64 | Sleutel tegen dubbele verwerking, in hoofdletters. Ook de enige manier waarop LinkRequest de aanvraag kan vinden. |
privacyAccepted | boolean | ja, true | Zonder deze sleutel wordt de aanvraag geweigerd. | |
privacyConsentVersion | string | ja | 20 | Welke privacyverklaring is geaccepteerd. In hoofdletters. |
contactName | string | 100 | Contactpersoon. | |
email | string | 100 | Waar de aanvrager de offerte verwacht. | |
companyName | string | 100 | ||
vatRegistrationNo | string | 20 | Wordt niet gecontroleerd. | |
phoneNo | string | 30 | ||
customerReference | string | 35 | De eigen referentie van de klant. Wordt Extern documentnr. op de offerte. | |
projectName | string | 100 | Sinds 1.0.44. De bouwplaats of het project waarvoor het staal is, bijvoorbeeld "Nieuwbouw Hoeve" of het adres van een bouwplaats. Wordt de eerste tekstregel van de offerte, Project: <naam>; zie Project, keuringsdocument en bewerking hieronder. Persoonsgegeven: na de bewaartermijn gewist, uit de aanvraag en uit de offerte. | |
languageCode | string | 10 | Een taalcode van Business Central, bijvoorbeeld NLB of FRB. De bevestigingsmail wordt in die taal geschreven. | |
address, address2, postCode, city, countryCode | string | 100 / 50 / 20 / 30 / 10 | Het factuuradres van de aanvrager. Het land is een ISO-code, bijvoorbeeld BE. | |
deliveryMethod | string | Pickup of Delivery. Elke andere waarde wordt Pickup. | ||
shipToCode | string | 10 | Een geregistreerd leveradres van de klant, uit MyDeliveryAddresses. Telt pas mee nadat LinkRequest het heeft gecontroleerd. | |
useShipToAddress | boolean | Het leveradres wijkt af van het factuuradres. | ||
shipToName, shipToAddress, shipToAddress2, shipToPostCode, shipToCity, shipToCountryCode | string | 100 / 100 / 50 / 20 / 30 / 10 | Het leveradres als tekst. Een ander land van levering kan de btw-behandeling van de offerte veranderen. | |
requestedDeliveryDate | string | YYYY-MM-DD. Een waarde die geen geldige datum is, laat de aanroep mislukken met [VOA-InvalidInput]. Laat de sleutel weg als er geen datum is; een lege tekenreeks mag ook. | ||
remarks | string | 2048 | Vrije tekst van de aanvrager. | |
ipHash | string | 64 | Zie IP-hash hierboven. | |
source | string | 50 | Het kanaal of de pagina. staffOrder heeft een bijzondere betekenis; zie hieronder. | |
assistedBy | string | 50 | De medewerker die een order via medewerker invoerde. Alleen bij een order via medewerker. | |
quoteRequestLines | array | Regelobjecten. Ontbreekt de sleutel of is het geen array, dan heeft de aanvraag geen regels. Elementen die geen object zijn, worden overgeslagen. |
CreateQuoteRequest: sleutels per regel
Objecten in quoteRequestLines, genummerd 10000, 20000, … in de volgorde van de array:
| Sleutel | Type | Max | Toelichting |
|---|---|---|---|
itemNo | string | 20 | Of het artikel bestaat, wordt bij het invoegen niet gecontroleerd; dat gebeurt als de binnendienst de aanvraag omzet. Of het mag worden aangevraagd, wordt wel gecontroleerd: een regel voor een artikel dat niet in de webcatalogus staat of niet online bestelbaar is, wordt geweigerd. Leeg bij een regel met vrije tekst, die altijd wordt geaccepteerd. |
variantCode | string | 10 | De afmeting. |
profileCode | string | 30 | Profiel uit Steel 365. |
qualityCode | string | 20 | Kwaliteit uit Steel 365. |
description | string | 100 | Zoals op de website getoond, of vrije tekst. |
quantity | number | Het gevraagde aantal. | |
unitOfMeasureCode | string | 10 | Bijvoorbeeld PCS, M of KG. Wordt bij de omzetting gecontroleerd. |
lengthMm | number | 0 betekent de standaardlengte van de afmeting. | |
widthMm | number | Alleen platen en strip. | |
thicknessMm | number | Alleen platen en strip. | |
lineRemarks | string | 250 | Bijvoorbeeld een zaaginstructie. |
inspectionCertificate | string | Sinds 1.0.44. Het keuringsdocument volgens EN 10204 dat bij deze regel wordt gevraagd: None (de standaard), Certificate22 (testrapport 2.2), Certificate31 of Certificate32 (inspectiecertificaat 3.1 of 3.2). Hoofdletters tellen niet. Elke andere waarde, zoals een onbekende naam, een getal als 31 of een object, wordt None en laat de aanroep niet mislukken. | |
processing | array van tekst | Sinds 1.0.44. De bewerkingen die bij deze regel worden gevraagd, zoveel als nodig uit: CutToLength, MitreCutting, Drilling, Punching, ThermalCutting (laser, plasma of autogeen), Bending, Blasting, Priming, Galvanizing (thermisch verzinken), Painting. Hoofdletters tellen niet. Onbekende waarden en elementen die geen tekst zijn, worden overgeslagen, dezelfde waarde twee keer wordt één keer opgeslagen, en een processing die geen array is, wordt genegeerd. Niets hiervan laat de aanroep mislukken. |
Een voorbeeldpayload, voordat die in payload wordt geserialiseerd:
{
"requestId": "3F2C9A1E7B0D4C5E",
"languageCode": "NLB",
"contactName": "Jan Example",
"companyName": "Example Ltd",
"email": "jan@example.com",
"customerReference": "Site 12",
"projectName": "New build Hoeve, Kerkstraat 12",
"address": "Example Street 1",
"postCode": "2000",
"city": "Antwerp",
"countryCode": "BE",
"deliveryMethod": "Delivery",
"useShipToAddress": true,
"shipToName": "Example Ltd",
"shipToAddress": "Yard Road 5",
"shipToPostCode": "2100",
"shipToCity": "Deurne",
"shipToCountryCode": "BE",
"requestedDeliveryDate": "2026-10-01",
"remarks": "Please call before delivery.",
"privacyAccepted": true,
"privacyConsentVersion": "2026-09",
"ipHash": "<64 hex characters>",
"source": "shop/en/quote-request",
"quoteRequestLines": [
{
"itemNo": "1000",
"variantCode": "6000",
"quantity": 4,
"unitOfMeasureCode": "PCS",
"lineRemarks": "Cut to 3 m",
"inspectionCertificate": "Certificate31",
"processing": ["CutToLength", "Drilling", "Galvanizing"]
}
]
}Wat er daarna gebeurt:
- De aanvraag komt in een eigen tabel terecht, niet in een verkoopofferte. De binnendienst zet hem met één actie om, alleen vanuit de Business Central-client.
- De mailtaak stuurt de aanvrager een bevestiging en de binnendienst een melding, afhankelijk van de schakelaars in de instellingen.
source=staffOrdermarkeert een order via medewerker: een lijst die een medewerker invoert voor een klant die er zelf is. Die wordt omgezet naar een verkooporder in plaats van een offerte, mag elk artikel bevatten dat het bedrijf verkoopt, en er wordt geen bevestiging of melding verstuurd. Bij afhalen krijgt de order een tekstregel met de naam van de medewerker uitassistedBy. Na de installatie staan orders via medewerker uit: zolang Orders via medewerker ingeschakeld uit staat, wordt de aanroep geweigerd metStaffOrdersDisableden wordt er niets opgeslagen. Versie 1.0.39 gebruikte hiervoorcounterVisit.- Is de bezoeker aangemeld, roep dan meteen na deze aanroep
LinkRequestaan om de klant van de sessie op de aanvraag te zetten. Het klantnummer zit nooit in de payload. shipToCodetelt pas mee nadatLinkRequestde code aan de hand van de klant van de sessie heeft gecontroleerd. Bij een aanvraag die niet op die manier is gekoppeld, weigert de omzetting de code tot de binnendienst hem met de hand bevestigt; stuur het adres daarom ook als tekst mee.
Project, keuringsdocument en bewerking
Sinds 1.0.44. Tot deze versie kon een website een bouwplaats, een 3.1-certificaat of "zagen, boren, verzinken" alleen in remarks en lineRemarks schrijven. De drie sleutels brengen ze als gegevens binnen: de binnendienst ziet het project op de aanvraagkaart en in de werklijst, en het keuringsdocument en de bewerking als kolommen op de aanvraagregels. Schrijf ze niet meer in de opmerkingen zodra u de sleutels stuurt, anders staan ze twee keer op de offerte.
Bij het omzetten komen ze als tekstregels op de verkoopofferte, of bij een order via medewerker op de verkooporder. Zo staan ze op het document dat de klant krijgt en op de werkbon van wie het staal zaagt:
| Sleutel | Waar op de offerte | Taal |
|---|---|---|
projectName | De eerste regel van het document: Project: <naam>, verdeeld over meer regels als de tekst langer is dan één regel. | Altijd Project: …, in elke taal. De opruimtaak zoekt deze regel woordelijk terug om hem te wissen, en een vertaalde regel zou ze misschien niet vinden. |
inspectionCertificate | Een tekstregel onder de artikelregel waar het bij hoort, bijvoorbeeld "Inspectiecertificaat 3.1 (EN 10204)". | De taal van de offerte, uit de Taalcode ervan. |
processing | Een tekstregel daaronder, bijvoorbeeld "Bewerking: Op lengte zagen, Boren, Thermisch verzinken". | De taal van de offerte. |
Ze staan vóór de eigen lineRemarks van de regel. Er wordt geen veld van de verkoopkop beschreven: Uw referentie bevat het aanvraagnummer en Extern documentnr. de customerReference, en Business Central heeft geen standaardveld voor een bouwplaats. Een keuringsdocument en een bewerking zijn geen persoonsgegevens en blijven op de offerte staan; het project wel, en de opruimtaak wist het uit de aanvraag en uit de offerte, net als het leveradres.
Het project staat ook in de twee mails over de aanvraag: de bevestiging aan de aanvrager heeft een regel met het project, met een label in de taal van de aanvraag, en de melding aan de binnendienst een rij Project. Zonder project staat het in geen van beide, en het keuringsdocument en de bewerking staan niet in de mails. De controle telt links in het project mee met die in remarks; de controle op een lange tekst zonder cijfer blijft bij remarks, omdat een project hooguit 100 tekens heeft. MyRequests en RequestLines geven het project terug.
Regels die worden geweigerd
Een regel voor een artikel dat niet in de webcatalogus staat, laat de hele aanroep mislukken met [VOA-ItemNotOnWebsite]; een regel voor een artikel dat wel in de catalogus staat maar niet online bestelbaar is (webQuotable is false), met [VOA-ItemNotQuotable]. Er wordt dan niets opgeslagen. Het eerste is meestal een verouderde pagina: ververs de catalogus en verwijder de regel. Het tweede hoort bij een artikel dat u als "prijs op aanvraag" toont, zonder bestelknop.
Een order via medewerker is hiervan uitgezonderd: bij source = staffOrder worden de twee catalogusweigeringen niet toegepast, omdat met de klant erbij alles verkocht mag worden wat het bedrijf verkoopt, ook wat bewust niet online staat.
CreateAccountRequest
Een aanvraag voor een portaalaccount. De binnendienst koppelt hem aan een klant en een rol, of wijst hem af.
| Sleutel | Type | Verplicht | Max | Toelichting |
|---|---|---|---|---|
requestId | string | aanbevolen | 64 | Sleutel tegen dubbele verwerking, in hoofdletters. |
privacyAccepted | boolean | ja, true | ||
privacyConsentVersion | string | ja | 20 | In hoofdletters. |
email | string | ja | 100 | Het adres waarmee de persoon zich zal aanmelden. |
contactName | string | 100 | ||
jobTitle | string | 50 | Helpt de binnendienst te kiezen tussen Inkoper en Meekijker. | |
companyName | string | 100 | Wordt niet gecontroleerd. | |
vatRegistrationNo | string | 20 | ||
phoneNo | string | 30 | ||
address, address2, postCode, city, countryCode | string | 100 / 50 / 20 / 30 / 10 | ||
existingCustomer | string | Yes, No of Unknown. Elke andere waarde wordt Unknown. | ||
statedCustomerNo | string | 20 | Het klantnummer dat de persoon opgeeft. Een aanwijzing voor de binnendienst, nooit een koppeling. | |
languageCode | string | 10 | Wordt gebruikt voor de mails aan deze persoon, ook voor latere aanmeldcodes. | |
remarks | string | 2048 | ||
ipHash | string | 64 | ||
source | string | 50 | De pagina waar de aanvraag vandaan kwam. |
Dubbele aanvragen voorkomen: requestId
Genereer per verzonden formulier één requestId en stuur dezelfde waarde mee als u die verzending opnieuw probeert. Een tweede aanvraag met een requestId die al bestaat, wordt geweigerd met HTTP 400, en de foutmelding bevat het merkteken [VOA-DUBBEL], de code DuplicateRequest en het nummer van de aanvraag die al is opgeslagen. Beschouw dat als geslaagd: de aanvraag staat in Business Central, alleen niet dankzij deze aanroep. De tekst van de melding wordt vertaald, dus controleer op het merkteken en nooit op de zin. De vergelijking is in feite niet hoofdlettergevoelig, omdat de waarde in hoofdletters wordt opgeslagen; zonder requestId is er geen controle op dubbele aanvragen. De regel geldt voor beide acties, elk binnen de eigen tabel.
Controle na ontvangst
Zodra een aanvraag is opgeslagen, controleert Business Central die: is het een herhaling van een eerdere aanvraag van hetzelfde adres binnen de ingestelde Termijn dubbele aanvragen (uren), en in hoeverre lijkt de aanvraag op ongewenst verkeer? Daarvan merkt de website niets. Het antwoord en de statuscode blijven gelijk, en de screening kan de aanroep nooit laten mislukken. Een aanvraag die op spam lijkt, wordt opgeslagen, met haar nummer beantwoord en net als elke andere aan de binnendienst getoond; ze wordt nooit geweigerd, verborgen of verwijderd. Bouw op de website dus niets dat voor een gemarkeerde aanvraag een ander antwoord verwacht: dat komt er niet. De ipHash doet hier wel ter zake: zonder die waarde kan het signaal over het aantal aanvragen per adres niet worden geteld. Hij blijft wat hij was: een pseudonieme HMAC die de website maakt; Business Central ziet nooit een IP-adres.
Het btw-nummer in MyDetails
Als u het offerteaanvraagformulier vooraf invult met MyDetails, krijgt u het btw-nummer van de klant terug onder de sleutel vatRegistrationNo, uit het veld waarin deze lokalisatie het bewaart: Btw-nummer, of bij een binnenlandse klant in een Belgische installatie Ondernemingsnummer (veld 11310 op de Klantenkaart, dat alleen in de Belgische lokalisatie bestaat). De sleutel en de betekenis ervan blijven gelijk; alleen het veld waaruit Business Central het nummer leest, verschilt per land.
Fouten
Alle fouten zijn AL-fouten, dus de aanroep mislukt met HTTP 400 en de melding in de body. De aanvraag wordt niet opgeslagen. Elke melding begint met een merkteken [VOA-…] dat niet wordt vertaald; zie Foutcodes verderop.
| Voorwaarde | Geldt voor | Code | De melding begint met |
|---|---|---|---|
payload is geen geldige JSON | beide | InvalidInput | The request is not valid JSON. |
| Aanvragen weigeren staat aan | beide | RequestsClosed | CustomerGate is not accepting requests at the moment. |
privacyAccepted is niet true | beide | PrivacyConsentMissing | A request cannot be accepted without agreement… |
privacyConsentVersion is leeg | beide | PrivacyConsentMissing | Include in privacyConsentVersion… |
email is leeg | accountaanvragen | InvalidInput | An account request without an e-mail address is not possible… |
requestId is al gebruikt | beide | DuplicateRequest | This submission has already been accepted under number… (bevat ook [VOA-DUBBEL]) |
| Een regel bevat een artikel dat niet in de webcatalogus staat | offerteaanvragen | ItemNotOnWebsite | Item … is not in the web catalog… |
| Een regel bevat een artikel dat niet online bestelbaar is | offerteaanvragen | ItemNotQuotable | Item … is available on request only… |
requestedDeliveryDate is geen geldige datum | offerteaanvragen | InvalidInput | The value of requestedDeliveryDate in the request is not valid. |
source is staffOrder terwijl Orders via medewerker ingeschakeld uit staat | alleen offerte | StaffOrdersDisabled | Staff orders are switched off in this company… |
| Geen nummerreeks in dit bedrijf | beide | SetupIncomplete | CustomerGate is not set up in this company… |
projectName, inspectionCertificate en processing (sinds 1.0.44) geven geen fout: een te lang project wordt net als elke andere tekst op 100 tekens afgekapt, en een waarde die geen van de waardenamen is, valt weg. De aanvraag zelf is de boodschap.
Bijlagen bij een aanvraag
Een tekening, een DXF, een foto van het oude onderdeel of een zaaglijst. Uploaden gaat via voaRequests, teruglezen via voaPortal: dat verschil is geen slordigheid, maar de kern van het ontwerp.
AddAttachment
Deze actie staat op voaRequests en niet op voaPortal, omdat een aanvraag van iedereen kan komen, ook van een bezoeker die niet is aangemeld, en juist van die bezoeker zou de binnendienst de tekening anders per e-mail moeten opvragen. Teruglezen gaat wel via voaPortal, waar een sessie beschikbaar is om het eigendom mee te controleren.
| Parameter | Toelichting |
|---|---|
requestNo | Het aanvraagnummer (WEB000123) of de requestId van de verzending. Gebruik bij voorkeur de requestId: een aanvraagnummer loopt op en is dus te raden. |
fileName | Naam met extensie, zonder map. Een voorafgaand pad wordt verwijderd; een naam die daarna nog een backslash, een schuine streep of een dubbele punt bevat, wordt geweigerd. Er worden hooguit 250 tekens bewaard. |
contentBase64 | Het bestand, base64-gecodeerd. Verder niets: een inhoudstype dat u meestuurt, zou toch niet worden vertrouwd. |
Het antwoord, na het ontleden van value:
| Sleutel | Type | Toelichting |
|---|---|---|
ok | boolean | Altijd true; bij een mislukking krijgt u een fout, geen ok:false. |
attachmentId | number | Het regelnummer binnen de aanvraag (10000, 20000, …). Voor uw eigen administratie; Attachment op voaPortal gebruikt in plaats daarvan de id uit MyAttachments. |
fileName | string | De naam zoals Business Central hem heeft opgeslagen, zonder eventuele map. |
size | number | De grootte in bytes, gemeten na het decoderen. |
contentHash | string | De SHA-256-hash van de inhoud, 64 hexadecimale tekens in kleine letters. |
- Twee keer dezelfde inhoud levert één bijlage op. Een tweede aanroep met dezelfde bytes voor dezelfde aanvraag geeft dezelfde
attachmentIdterug en slaat verder niets op, dus na een time-out kunt u een upload veilig opnieuw proberen. De naam van de eerste upload blijft staan. - Status: geaccepteerd zolang de aanvraag
NewofInProgressis. Geweigerd zodra zeConverted,RejectedofSpamis, en geweigerd voor een aanvraag waarvan de persoonsgegevens zijn gewist. - De grootte wordt twee keer gecontroleerd: eerst op de lengte van de base64-tekenreeks, vóór het decoderen, zodat een enorme payload de sessie niet kan uitputten, en daarna op de echte bytes.
- Het inhoudstype wordt nooit van u overgenomen. CustomerGate leidt het af uit de extensie; een onbekende extensie krijgt
application/octet-stream.
De instellingen, en wat geen instelling kan wijzigen
Vijf velden op CustomerGate-instellingen, onder Bijlagen, bepalen wat AddAttachment accepteert. Ze gelden per bedrijf, dus neem de standaardwaarden hieronder niet over in uw formulier. Sinds 1.0.44 geeft voaPing onder attachments de waarden die in dit bedrijf gelden: of uploaden überhaupt werkt, de grenzen voor grootte en aantal met de terugvalwaarde voor 0 al toegepast, de toegestane soorten precies zoals AddAttachment ze controleert, de weigerlijst en de soorten waarvan de eerste bytes worden gecontroleerd. Ze komen uit dezelfde regels als de controle bij het uploaden, dus een formulier dat erop is gebouwd, accepteert precies wat Business Central accepteert. Lees ze als uw toepassing start, samen met de rest van voaPing. Vóór 1.0.44: vraag de beheerder hoe ze zijn ingesteld voordat u een klant iets belooft.
| Instelling | Standaard | Toegestaan | Effect |
|---|---|---|---|
| Bijlagen ingeschakeld | aan | Als dit uit staat, mislukt AddAttachment met een fout. Bestanden die al zijn opgeslagen, blijven leesbaar via MyAttachments en Attachment. | |
| Maximale grootte bijlage (KB) | 10240 | 0 t/m 20480 | Per bestand, gemeten op de gedecodeerde bytes. Bij 0 geldt 10240. |
| Toegestane soorten bijlagen | pdf,png,jpg,jpeg,dxf,dwg,step,stp,xlsx,csv | Extensies zonder punt, gescheiden door komma's. Alle andere worden geweigerd. | |
| Maximum aantal bijlagen per aanvraag | 10 | 0 t/m 100 | Per aanvraag. Bij 0 geldt 10. |
| Bijlagen kopiëren naar verkoopdocument | aan | Of de omzetting de bestanden naar de bijlagen van het verkoopdocument kopieert. Niet zichtbaar via een webservice. |
De weigerlijst exe, dll, js, vbs, ps1, bat, cmd, scr, msi, jar, sh, html, htm, svg staat in de code, wordt toegepast op elk deel van de naam tussen punten (drawing.exe.pdf wordt geweigerd) en gaat voor op Toegestane soorten bijlagen. Geen enkele instelling kan die lijst opheffen. Hetzelfde geldt voor de controle op de eerste bytes: bij pdf, png, jpg, jpeg, xlsx, dwg, step en stp moet de inhoud beginnen zoals de extensie belooft. dxf en csv hebben geen vast begin en worden niet op inhoud gecontroleerd.
CustomerGate controleert het bestandstype, niet de integriteit. Een bestand waarvan de eerste bytes bij de extensie passen, wordt opgeslagen en precies zo teruggegeven als het binnenkwam, ook als het afgekapt of anderszins beschadigd is, net als een bestand dat per e-mail binnenkomt. Business Central mag de inhoud met opzet niet interpreteren, zodat een beschadigde afbeelding nooit midden in een upload tot een platformfout kan leiden. Wilt u meer dan een typecontrole, valideer het bestand dan op de website voordat u AddAttachment aanroept.
MyAttachments
voaPortal_MyAttachments(token, requestNo) geeft de bestanden terug van één offerteaanvraag van de klant achter deze sessie: wat de klant zelf heeft gestuurd, plus wat de binnendienst heeft toegevoegd. Het eigendom wordt op de aanvraag gecontroleerd, want een bijlage heeft geen klantnummer: de aanvraag moet van de klant van deze sessie zijn en mag niet geanonimiseerd of als spam gemarkeerd zijn. Dat zijn dezelfde drie voorwaarden als bij MyRequests, zodat een klant nooit in het ene scherm ziet wat in het andere is verdwenen. Een aanvraag van iemand anders geeft hetzelfde "" als een aanvraag die niet bestaat.
| Sleutel | Type | Toelichting |
|---|---|---|
requestNo | string | De aanvraag, ongewijzigd teruggegeven. |
attachments | array | De bestanden, de oudste eerst. |
Elk element:
| Sleutel | Type | Toelichting |
|---|---|---|
id | string | De SystemId van de bijlage, in kleine letters en zonder accolades. Dit geeft u mee aan Attachment. |
fileName | string | De naam zoals hij is opgeslagen. |
extension | string | Kleine letters, zonder punt. |
contentType | string | Het MIME-type dat CustomerGate uit de extensie heeft afgeleid. |
size | number | Bytes. |
contentHash | string | SHA-256-hash van de inhoud, 64 hexadecimale tekens in kleine letters. |
uploadedAt | string | Tijdstempel. |
origin | string | PortalUser of InsideSales. |
De inhoud staat nooit in deze lijst: tien tekeningen kunnen samen al honderd megabyte beslaan. Haal de bestanden één voor één op met Attachment.
Attachment
voaPortal_Attachment(token, attachmentId) geeft de inhoud van één bestand terug, base64-gecodeerd, in dezelfde vorm als Document. attachmentId is de id uit MyAttachments, niet de attachmentId die AddAttachment heeft teruggegeven. Dat is met opzet: een regelnummer is 10000, 20000, 30000 en dus te raden, en een aanvraagnummer ernaast ook. De eigendomscontrole vindt hoe dan ook plaats, dus raden levert niets op; met een sleutel die niet te raden is, begint een poging niet eens.
| Sleutel | Type | Toelichting |
|---|---|---|
fileName | string | |
contentType | string | |
size | number | Bytes. |
contentHash | string | SHA-256-hash, zodat u kunt controleren wat u hebt ontvangen. |
base64 | string | Het bestand. |
Het antwoord is "" bij een ongeldige sessie, een id die geen GUID is, een onbekende id, een bijlage van een andere klant, of een bijlage zonder inhoud: allemaal hetzelfde lege antwoord.
Fouten bij het uploaden
AddAttachment heeft eigen fouten, allemaal HTTP 400 met de melding in de body. Er wordt niets opgeslagen. MyAttachments en Attachment geven nooit een fout: ze antwoorden "".
| Voorwaarde | De melding begint met |
|---|---|
| Bijlagen staan uit | Attachments are switched off. |
requestNo hoort bij geen enkele aanvraag | There is no web request … |
| De aanvraag heeft de status Omgezet, Afgewezen of Spam | Request … no longer accepts files: its status is … |
| De aanvraag is geanonimiseerd | Request … has been anonymized and no longer accepts files. |
fileName is leeg | A file without a name cannot be accepted. |
fileName bevat nog een map of een dubbele punt | The file name … is not a file name… |
fileName heeft geen extensie, of een van meer dan 10 tekens | The file … has no extension… |
| Een deel van de naam staat op de weigerlijst | Files of type … are never accepted… |
| De extensie staat niet in Toegestane soorten bijlagen | Files of type … are not accepted. The allowed types are: … |
| Groter dan Maximale grootte bijlage (KB) | The file … is larger than the maximum of … KB. |
contentBase64 is geen geldige base64 | The content of … is not valid base64. |
| Lege inhoud | The file … is empty. |
| De eerste bytes passen niet bij de extensie | The content of … is not a … file. |
| De aanvraag heeft al het maximale aantal bestanden | Request … already has … files, and that is the maximum. |
voaPortal: het klantenportaal
Elke actie is POST .../ODataV4/voaPortal_{Procedure}?company={company id}. Alleen deze vierentwintig procedures zijn aanroepbaar; elke andere procedure in de codeunit is lokaal.
| Procedure | Parameters | Geeft terug | Rol | Wijzigt gegevens |
|---|---|---|---|---|
RequestCode | email, ipHash | boolean | iedereen | maakt een code, verstuurt een mail |
SignIn | email, code, ipHash | string: token of "" | iedereen | gebruikt de code, maakt een sessie |
ReadSession | token | JSON-string of "" | beide | verlengt de sessie |
SignOut | token | boolean (altijd true) | beide | trekt de sessie in |
MyDetails | token | JSON-string of "" | beide | verlengt de sessie |
MyDeliveryAddresses | token | JSON-string of "" | Inkoper | verlengt de sessie |
LinkRequest | token, requestId | boolean | Inkoper | koppelt een aanvraag aan de klant |
MyRequests | token | JSON-string of "" | beide | verlengt de sessie |
RequestLines | token, requestNo | JSON-string of "" | beide | verlengt de sessie |
MyQuotes | token | JSON-string of "" | beide | verlengt de sessie |
MyOrders | token | JSON-string of "" | beide | verlengt de sessie |
MyShipments | token | JSON-string of "" | beide | verlengt de sessie |
DocumentLines | token, documentType, documentNo | JSON-string of "" | beide | verlengt de sessie |
AcceptQuote | token, quoteNo, ipHash | JSON-string | Inkoper | legt een akkoord vast |
AcceptQuoteWithDetails | token, quoteNo, ipHash, poNumber, requestedDeliveryDate | JSON-string | Inkoper | legt een akkoord vast met twee extra waarden |
RejectQuote | token, quoteNo, ipHash, reasonCode, remark | JSON-string | Inkoper | legt een afwijzing vast |
MyDocuments | token | JSON-string of "" | beide | verlengt de sessie |
Document | token, documentType, documentNo | JSON-string of "" | beide | verlengt de sessie |
MyAttachments | token, requestNo | JSON-string of "" | beide | verlengt de sessie |
Attachment | token, attachmentId | JSON-string of "" | beide | verlengt de sessie |
SwitchCustomer | token, customerNo, ipHash | JSON-string | beide | zet de sessie over naar een andere klant van hetzelfde adres |
MyNotificationSettings | token | JSON-string of "" | beide | verlengt de sessie |
SetNotificationSettings | token, settings | JSON-string of "" | beide | wijzigt de eigen berichtkeuzes van de aanroeper |
InviteColleague | token, email, name, role, privacyConsentVersion | JSON-string | Inkoper | maakt een accountaanvraag voor een collega |
Harde grenzen
| Lijst | Maximum |
|---|---|
MyRequests | 200 aanvragen |
MyQuotes, MyOrders, MyShipments | elk 100 rijen, actueel en gearchiveerd samen; truncated zegt of er meer waren |
MyDeliveryAddresses | 100 adressen |
DocumentLines | 500 regels |
RequestLines | 500 regels |
MyDocuments, archief van Steel 365 | 200 documenten, uit hooguit 1.000 bekeken archiefrijen |
MyDocuments, uit rapporten | 100 per documentsoort |
MyAttachments | zoveel bestanden als de aanvraag heeft; Maximum aantal bijlagen per aanvraag begrenst dat tot 100 |
ReadSession, MyDetails | customers heeft geen vast maximum; in de praktijk zijn het er één of twee |
RequestCode, SignIn, SignOut
RequestCode geeft altijd true terug, na minstens 1,5 seconde; roep hem niet automatisch opnieuw aan, want elke aanroep kan een mail versturen. SignIn geeft het sessietoken van 64 tekens terug, of "" als de code verkeerd, verlopen of al gebruikt is, of als de portaalgebruiker of de klant niet meer is toegelaten; het antwoord zegt nooit welk geval het is. Een juiste code voor een gebruiker die intussen is gedeactiveerd, wordt opgebruikt en geeft "" terug. SignOut trekt de sessie in als het token bestaat en geeft altijd true terug, ook bij een onbekend token; verwijder de cookie hoe dan ook.
ReadSession
Wie er achter het token zit. Roep deze procedure aan bij elke paginaweergave van het portaal: ze controleert de sessie, de portaalgebruiker en de klant, en verlengt de sessie.
| Sleutel | Type | Toelichting |
|---|---|---|
email | string | Het adres van de portaalgebruiker. |
customerNo | string | De klant van deze sessie. Business Central geeft die door aan de website, nooit andersom. |
customerName | string | Klantnaam, of "". |
role | string | Buyer of Viewer. |
weightType | string | Het gewichtstype uit Steel 365 van de klant (Theoretical, Trade, GermanTrade), of "" zonder Steel 365 of als het niet is ingevuld. |
expiresAt | string | Tijdstip waarop de sessie verloopt als hij niet meer wordt gebruikt. |
customers | array | Elke klant waarvoor dit adres mag optreden, elk als {"no": …, "name": …, "role": …}. Bij een geldige sessie altijd aanwezig en nooit leeg; in het gewone geval één element. Ingetrokken koppelingen en klanten die niet tot het portaal zijn toegelaten, worden weggelaten, dus naar alles in deze lijst kan worden gewisseld. |
{
"email": "buyer@example.com",
"customerNo": "C00010",
"customerName": "Example Ltd",
"role": "Buyer",
"weightType": "",
"expiresAt": "2026-09-19T22:30:00.000Z",
"customers": [
{"no": "C00010", "name": "Example Ltd", "role": "Buyer"},
{"no": "C00044", "name": "Example Holding", "role": "Viewer"}
]
}MyDetails
De bedrijfsgegevens van de klant van de sessie, om het offerteaanvraagformulier vooraf in te vullen. Elk veld is een tekenreeks en kan "" zijn. Het klantnummer zit er bewust niet in, en er zijn geen prijzen, kortingen of kredietgegevens. De sleutels zijn company, vatRegistrationNo, address, address2, postCode, city, country, phoneNo, email (het adres van de portaalgebruiker, niet dat op de Klantenkaart) en role. Daarnaast bevat het antwoord customers, dezelfde array als in ReadSession, zodat een formulier kan tonen namens wie iemand de aanvraag doet. Die array zegt welke klanten gekozen mogen worden, niet welke actief is: dat is customerNo in ReadSession.
MyDeliveryAddresses
De geregistreerde leveradressen van de klant van de sessie, alleen voor een Inkoper: {"addresses": [{"code", "name", "address", "address2", "postCode", "city", "country"}]}, allemaal tekenreeksen. Een Meekijker krijgt "", net als bij een ongeldige sessie. Het factuuradres staat niet in deze lijst, maar in MyDetails. Stuur de gekozen code als shipToCode mee in CreateQuoteRequest.
LinkRequest
Zet de klant van de sessie op een offerteaanvraag die net is verstuurd; roep deze procedure dus meteen na CreateQuoteRequest aan, met dezelfde requestId. Ze geeft alleen true terug als de sessie geldig is, de rol Inkoper is, requestId (getrimd, in hoofdletters, hooguit 64 tekens) niet leeg is, en er een offerteaanvraag met dat Aanvraag-id bestaat die nog geen klant heeft. Daarna zet ze de klant op de aanvraag, behoudt ze shipToCode alleen als dat een leveradres van die klant is en wist ze de code anders (de adrestekst blijft staan), en zet ze de Bron van de aanvraag op portal. Een false is geen fout: de aanvraag bestaat hoe dan ook, en de binnendienst neemt haar in behandeling zoals elke andere.
MyRequests
De offerteaanvragen die aan de klant van de sessie zijn gekoppeld, de nieuwste eerst, hooguit 200. Geanonimiseerde aanvragen en aanvragen die als spam zijn gemarkeerd, blijven erbuiten.
| Sleutel | Type | Toelichting |
|---|---|---|
no | string | Aanvraagnummer. |
receivedAt | string | Tijdstempel. |
status | string | New, InProgress, Converted, Rejected, of "" voor een status die deze versie niet kent. |
quoteNo | string | Optioneel. Het verkoopdocument dat uit de aanvraag is gemaakt; bij een order via medewerker een ordernummer. |
documentType | string | Aanwezig bij quoteNo. quote, of order bij een order via medewerker. Geeft aan naar welke lijst quoteNo moet linken. |
quoteNoStatus | string | Aanwezig bij quoteNo. active bij een actueel document van deze klant, completed bij een vrijgegeven offerte in het offertearchief of een gearchiveerde order, anders "". |
lines | number | Aantal aanvraagregels. |
projectName | string | Sinds 1.0.44. Het project dat de aanvrager noemde (projectName van CreateQuoteRequest), of "". Altijd aanwezig. |
RequestLines
De regels van een van uw eigen offerteaanvragen, in de vorm waarin CreateQuoteRequest ze weer accepteert. Hierop is "nog eens hetzelfde bestellen" gebouwd: de website leest de regels van de aanvraag van vorige maand en vult er een nieuw formulier mee. Het eigendom wordt op de aanvraag gecontroleerd, met precies dezelfde drie voorwaarden als bij MyAttachments. Een omgezette aanvraag zit er met opzet bij: juist de aanvraag die vorige maand een order werd, wil iemand herhalen, dus er is geen filter op status behalve spam.
Het antwoord is {"requestNo": …, "lines": [ … ]}, met de regels in volgorde van regelnummer, hooguit 500. Elk element heeft lineNo (ter informatie, stuur het niet terug) en verder de sleutels die CreateQuoteRequest verwacht: itemNo, variantCode, profileCode, qualityCode, description, quantity, unitOfMeasureCode, lengthMm, widthMm, thicknessMm, lineRemarks en sinds 1.0.44 inspectionCertificate (None, Certificate22, Certificate31 of Certificate32) en processing (een array met de waardenamen in de volgorde van de enum, [] als er geen zijn). Sinds 1.0.44 bevat het antwoord ook projectName, het project van de aanvraag of ""; een herhaalde bestelling is vaak voor een andere bouwplaats, dus bied het aan als suggestie en niet als vaststaand gegeven. Een eigen aanvraag zonder regels geeft {"requestNo": "WEB000123", "lines": []}.
MyQuotes
De verkoopoffertes van de klant van de sessie: eerst de actuele offertes, het hoogste nummer eerst, daarna offertes die alleen nog in het offertearchief bestaan (omgezet of verwijderd), alleen vrijgegeven versies, per nummer de hoogste gearchiveerde versie. Samen hooguit 100 rijen. Het antwoord is {"quotes": [ … ], "truncated": <Boolean>}.
| Sleutel | Type | Toelichting |
|---|---|---|
no | string | Offertenummer. |
documentDate | string | Datum. |
validUntil | string | De geldigheidsdatum van de offerte, anders de vervaldatum uit Steel 365, anders "". |
yourReference | string | De referentie van de klant: Extern documentnr. |
requestNo | string | De webaanvraag waaruit deze offerte is gemaakt, of "". Business Central geeft alleen een nummer terug van een bestaande aanvraag van deze offerte of deze klant; vrije tekst in Uw referentie wordt nooit doorgegeven. Ga niet uit van een vast nummerpatroon: de nummerreeks is instelbaar. |
status | string | Open, Released, of "" bij elke andere status. |
lines | number | Aantal artikelregels. |
totalWeightKg | number | Optioneel, alleen met Steel 365. Totaalgewicht in kg. |
hasDocument | boolean | true alleen als Document deze offerte kan afdrukken. Bij gearchiveerde rijen altijd false. |
accepted | boolean | Er is via het portaal een akkoord vastgelegd. |
acceptedAt | string | Sinds 1.0.44. Het moment van dat akkoord, hetzelfde tijdstempel dat AcceptQuote teruggaf, of "". |
rejectedAt | string | Sinds 1.0.44. Het moment waarop de klant de offerte met RejectQuote afwees, hetzelfde tijdstempel dat die aanroep teruggaf, of "". |
rejectionReason | string | Sinds 1.0.44. De reden die bij die afwijzing is opgegeven: TooExpensive, DeliveryTooLate, OrderedElsewhere, NoLongerNeeded, Other, of NotStated als de website een reden stuurde die de app niet kent. "" zonder afwijzing. De eigen opmerking van de klant komt niet terug; die is voor de binnendienst. |
comment | string | Alleen bij actuele rijen: de opmerking die de binnendienst voor de klant heeft geschreven. Nooit de interne werkbeschrijving. |
orderNo, orderNoStatus | string | Bij gearchiveerde rijen, optioneel: de order waarin deze offerte is omgezet, uit het documentarchief van Steel 365, met active, completed of "". Link alleen naar de order als dit niet "" is. |
Regels voor het lezen van het antwoord van de klant (sinds 1.0.44):
- Lees deze sleutels na elke
AcceptQuoteenRejectQuoteen bouw de knoppen erop: geen knoppen alsacceptedtrueis ofrejectedAtniet""is. Vóór 1.0.44 toonde een website die na een afwijzing ververste beide knoppen weer, en een tweede afwijzing gafalready. - De vier sleutels zijn er altijd, gevuld of
""offalse. Een gearchiveerde offerte heeft ze ook: een offerte die is afgewezen en daarna is omgezet of verwijderd, toont de afwijzing nog steeds. - Beide kunnen gevuld zijn.
RejectQuoteweigert een offerte die al geaccepteerd is, maarAcceptQuoteweigert geen offerte die is afgewezen: een klant die nee zei en van gedachten verandert, kan nog accepteren. Zijn beide gevuld, dan is het latere tijdstempel het laatste woord van de klant. - De tijdstempels zijn ISO 8601 in UTC, zoals elk tijdstempel hier; vergelijk ze als momenten, niet als tekst.
MyOrders
De verkooporders van de klant van de sessie: actuele orders, het hoogste nummer eerst, daarna orders die alleen nog in het orderarchief bestaan, per nummer de hoogste gearchiveerde versie, met elke status. Samen hooguit 100 rijen, als {"orders": [ … ], "truncated": <Boolean>}.
| Sleutel | Type | Toelichting |
|---|---|---|
no | string | Ordernummer. |
quoteNo, quoteNoStatus | string | De offerte waaruit de order is voortgekomen, met active, completed of "". Bij gearchiveerde rijen komt die uit het documentarchief van Steel 365. |
orderDate, requestedDeliveryDate, promisedDeliveryDate | string | Datums uit de kop van de order. Per regel staan ze in DocumentLines. |
yourReference | string | De referentie van de klant (Extern documentnr.). |
status | string | Open, Released of "". |
completelyShipped | boolean | Actueel: het eigen veld van de order. Gearchiveerd: per regel herrekend uit geboekte verzendregels. |
lines | number | Aantal artikelregels. |
hasDocument | boolean | Zoals bij MyQuotes. |
comment | string | Alleen bij actuele rijen: de opmerking voor de klant. |
MyShipments
De geboekte verkoopverzendingen van de klant van de sessie, de nieuwste eerst, hooguit 100, als {"shipments": [ … ], "truncated": <Boolean>}.
| Sleutel | Type | Toelichting |
|---|---|---|
no | string | Verzendnummer. |
deliveryDate | string | Boekingsdatum van de verzending. |
orderNo, orderNoStatus | string | De order waar de verzending bij hoort, met active, completed of "". |
yourReference | string | De referentie van de klant (Extern documentnr.). |
shipToAddress | object | {"name", "postCode", "city"}. Straat en huisnummer worden niet meegegeven. |
lines | number | Aantal artikelregels. |
hasDocument | boolean | Zoals bij MyQuotes. |
packageTrackingNo | string | Het pakketnummer van de vervoerder, of "". |
shippingAgentCode | string | De code van de expediteur op de verzending, of "". |
trackingUrl | string | Een kant-en-klare link naar de track-and-tracepagina van de vervoerder, of "". |
Die drie sleutels zijn altijd aanwezig en vaak leeg: een staalhandel levert het meeste met eigen vrachtwagens, en dan is er geen vervoerder en geen pakketnummer. Lees "" als "geen track-and-trace voor deze verzending" en toon geen knop. Business Central stelt trackingUrl samen uit het internetadres op de kaart van de expediteur, dat een plaatshouder voor het pakketnummer bevat, met dezelfde hulpfunctie die de standaardactie in Business Central zelf gebruikt. Het veld is leeg als er geen expediteurcode is, de expediteur niet meer bestaat, de expediteur geen internetadres heeft of er geen pakketnummer is: een link naar een zoekpagina van een vervoerder waarop niets is ingevuld, stelt alleen maar teleur. Laat het sjabloon het schema weg, dan wordt dat https://. CustomerGate geeft het sjabloon zelf nooit prijs, alleen de kant-en-klare link.
DocumentLines
Parameters: documentType is quote, order of shipment (niet hoofdlettergevoelig; elke andere waarde geeft ""), en documentNo is het documentnummer. Het eigendom wordt gecontroleerd voordat er ook maar één regel wordt gelezen. Bij quote en order wordt eerst het actuele document geprobeerd; bestaat dat niet, dan de hoogste versie in het archief, bij offertes alleen vrijgegeven versies. Alleen regels van het type Artikel komen terug, hooguit 500; tekst- en opmerkingsregels worden weggelaten. Het antwoord is {"lines": [ … ]}.
| Sleutel | Type | Wanneer | Toelichting |
|---|---|---|---|
lineNo | number | altijd | Regelnr. |
itemNo | string | altijd | Artikelnummer. |
description | string | altijd | |
format | string | altijd | Variantcode: de afmeting. |
quantity | number | zonder Steel 365 | Aantal. |
unitOfMeasureCode | string | zonder Steel 365 | Eenheid van quantity. |
outstandingQuantity, quantityShipped | number | zonder Steel 365, actuele orderregels | |
profile, grade, formatDescription | string | Steel 365, optioneel | Item Profile Code DMX, Quality Code DMX, Item Variant Description DMX. |
unit | string | Steel 365, optioneel | Doc. Unit of Measure Code DMX, de onvertaalde optienaam. Geeft aan welk van de drie aantallen leidend is; de referentiewebsite verwacht PCS, KG of MTR. |
quantityPieces, quantityMeters, quantityKg | number | Steel 365 | Quantity Pieces DMX, Quantity Length DMX, Quantity Weight DMX. |
weightType | string | Steel 365, optioneel | Theoretical, Trade of GermanTrade. |
lengthMm, widthMm, thicknessMm | number | Steel 365, optioneel | Sinds 1.0.44. Length DMX, Width DMX en Thickness DMX van de artikelvariant van de regel (itemNo + format), in millimeters zoals DXSteel ze opslaat: dezelfde getallen die voaVariants teruggeeft als DXLength, DXWidth en DXThickness. |
customFormat | boolean | Steel 365, optioneel | Sinds 1.0.44. true als de variant een eenmalige afmeting is (Format Type DMX is OneTime): een maat die DXSteel voor deze order maakte, bijvoorbeeld een balk op 9000 mm gezaagd. false bij een handelsafmeting of een niet-handelsafmeting. |
outstandingPieces, outstandingMeters, outstandingKg, shippedPieces, shippedMeters, shippedKg | number | Steel 365, actuele orderregels | Openstaande en verzonden aantallen. |
requestedDeliveryDate | string | actuele orderregels | Wat de klant voor deze regel heeft gevraagd. "" als het niet is ingevuld. |
promisedDeliveryDate | string | actuele orderregels | Wat er voor deze regel is toegezegd. "" als het niet is ingevuld. |
lineStatus | string | actuele orderregels | Hoe ver deze regel is: Open, PartlyShipped, Shipped of Invoiced. |
Aandachtspunten bij het lezen van regels:
- Tekstsleutels van Steel 365 worden weggelaten als ze leeg zijn, terwijl getalsleutels van Steel 365 aanwezig zijn zodra het veld bestaat;
0betekent dus nul en een ontbrekende sleutel betekent onbekend. Toon dan een streepje, geen0. - Met Steel 365 zijn stuks, meters en kilo's drie aparte aantallen: tel ze nooit op en kies er niet zelf een;
unitzegt welk aantal leidend is. - Openstaande en verzonden waarden bestaan alleen bij actuele orderregels; reken ze niet zelf uit.
requestedDeliveryDate,promisedDeliveryDateenlineStatusbestaan om dezelfde reden alleen bij actuele orderregels: op een offerteregel is niets besteld, dus elke uitspraak over levering zou over iets gaan dat nog niet bestaat.- De twee datums komen van de regel en niet van de kop. Dat is juist de bedoeling: bij een deellevering schuift de ene regel wel en de andere niet, en de kop vertelt dan het gemiddelde van twee verhalen.
MyOrdersblijft voor het lijstoverzicht de datums van de kop geven. lineStatusis afgeleid, en wel uit de standaardaantallen (Aantal, Verzonden aantal, Gefactureerd aantal) die Business Central op elke verkoopregel bijhoudt, met of zonder Steel 365. Het betekent dus in beide soorten omgevingen hetzelfde, terwijl de aantallen ernaast dat niet doen. De controles gaan van verst naar minst ver: gefactureerd gaat voor verzonden, verzonden voor deels verzonden. Een regel met een aantal van nul is altijdOpenen nooitShipped.- Er komt nooit een prijs, korting, marge of bedrag terug.
- De maat van een regel (
lengthMm,widthMm,thicknessMm,customFormat, sinds 1.0.44) komt van de artikelvariant van de regel, bij elke soort regel: offerte, order of zending, actueel of gearchiveerd. De vier sleutels volgen de regel voor Steel 365 hierboven: aanwezig zodra DXSteel het veld heeft, dus0betekent nul (een plaat heeft een breedte, een balk niet) en een ontbrekende sleutel betekent onbekend. Ze ontbreken ook als de regel geen variantcode heeft of de variant niet meer bestaat. Zonder Steel 365 zijn ze er nooit.
{
"lines": [
{"lineNo": 10000, "itemNo": "1000", "description": "HEA 200 S235JR", "format": "12000",
"profile": "HEA200", "grade": "S235JR", "unit": "PCS", "quantityPieces": 4,
"quantityMeters": 48, "quantityKg": 2035.2, "weightType": "Theoretical",
"lengthMm": 12000, "widthMm": 0, "thicknessMm": 0, "customFormat": false,
"requestedDeliveryDate": "2026-11-03", "promisedDeliveryDate": "2026-11-05",
"lineStatus": "PartlyShipped"}
]
}Een regel opnieuw bestellen
Sinds 1.0.44. Om een regel van een eerdere offerte of order in een nieuwe CreateQuoteRequest te zetten, stuurt u:
| Regelsleutel van `CreateQuoteRequest` | Uit `DocumentLines` |
|---|---|
itemNo | itemNo |
variantCode | format, alleen als customFormat false is (een handelsafmeting of een niet-handelsafmeting uit de catalogus). Laat de sleutel weg bij een eenmalige afmeting. |
lengthMm, widthMm, thicknessMm | lengthMm, widthMm, thicknessMm, als customFormat true is. Stuur ze zoals ze binnenkwamen; een 0 wordt overgeslagen. |
profileCode, qualityCode | profile, grade |
description | description |
quantity, unitOfMeasureCode | Het leidende aantal: unit zegt welk van quantityPieces, quantityKg en quantityMeters dat is. Stuur het met de eenheidscode die uw formulier voor die eenheid gebruikt, net als bij een nieuwe aanvraag. Zonder Steel 365: quantity en unitOfMeasureCode. |
Waarom niet de variantcode van een eenmalige afmeting: dat is een variant die DXSteel voor één order maakte, geen maat uit de catalogus. Die is niet bedoeld om opnieuw aan te bieden, en kan worden geblokkeerd of opgeruimd. Met het artikelnummer en de afmetingen en zonder variantCode doet de omzetting wat een verkoper doet die de maat in de offerteregel typt: DXSteel zoekt eerst een bestaande variant met precies deze afmetingen en maakt pas een nieuwe eenmalige afmeting als die er niet is. Die weg wordt geaccepteerd bij binnenkomst, waar de controle op de catalogus naar het artikel kijkt en niet naar de variant, en bij het omzetten, waar een artikel met meerdere maten zonder variantcode alleen wordt geweigerd als er ook geen afmeting is opgegeven.
Een regel van een eerdere aanvraag komt precies in deze vorm terug uit RequestLines; gebruik die als u het aanvraagnummer hebt.
AcceptQuote
Legt vast dat de klant een vrijgegeven verkoopofferte accepteert. Alleen voor een Inkoper. Er wordt in Business Central niets omgezet of vrijgegeven: het akkoord is een signaal voor de binnendienst, die de offerte met de hand omzet. De procedure geeft altijd een object terug met outcome (recorded, already, expired of refused), acceptedAt (een tijdstempel, of "") en, alleen bij refused of expired, errorCode. De controles, op volgorde:
| Controle | Uitkomst |
|---|---|
| Sessie niet geldig | refused |
| De rol is geen Inkoper | refused |
quoteNo is leeg of langer dan 20 tekens | refused |
| Geen actuele offerte met dat nummer voor deze klant | refused |
| De status van de offerte is niet Vrijgegeven | refused |
| Er bestaat al een akkoord voor deze offerte | already, met de oorspronkelijke acceptedAt |
| De geldigheidsdatum is ingevuld en ligt vóór vandaag | expired |
| Een andere aanroep legt op hetzelfde moment een akkoord voor deze offerte vast | already, acceptedAt = "" |
| Anders | recorded, acceptedAt = nu |
refused zegt nooit waarom, en already is geen fout: toon dan dat het akkoord binnen is. Probeer deze aanroep niet automatisch opnieuw. Bij het akkoord worden het offertenummer, de klant, het e-mailadres van de portaalgebruiker, het moment, de IP-hash, de documentdatum, het bedrag inclusief btw, de valuta, het aantal regels, de laatste archiefversie en een vingerafdruk van de regels bewaard, zodat duidelijk blijft waarmee is ingestemd als de offerte later verandert. De mailtaak stuurt daarna de klant een bevestiging en de binnendienst een melding.
AcceptQuoteWithDetails
Hetzelfde akkoord als AcceptQuote, met de twee dingen die een klant erbij wil zeggen: zijn inkoopordernummer en de datum waarop hij het staal wil. Elke controle, elke uitkomst en elke sleutel van het antwoord is gelijk; alleen de twee parameters zijn nieuw.
Waarom een tweede procedure en niet twee extra parameters. OData koppelt parameters op naam, dus een aanroep van voaPortal_AcceptQuote die de nieuwe namen niet meestuurt, zou een kale 400 opleveren. Een parameter toevoegen aan een gepubliceerde actie breekt daarom bestaande aanroepen. AcceptQuote behoudt zijn drie parameters, blijft werken en roept deze procedure aan met twee lege waarden.
| Parameter | Toelichting |
|---|---|
poNumber | Het inkoopordernummer van de klant, hooguit 35 tekens. Een langere waarde wordt afgekapt. |
requestedDeliveryDate | YYYY-MM-DD. Een waarde die geen datum is, laat de aanroep niet mislukken: het akkoord is het belangrijkste en mag niet verloren gaan door een typefout in een veld ernaast. De datum blijft dan leeg, net alsof de parameter was weggelaten. Dit wijkt met opzet af van CreateQuoteRequest, waar dezelfde waarde de aanroep wel laat mislukken, omdat de datum daar deel uitmaakt van wat er wordt gevraagd. |
Waar de twee waarden terechtkomen: op het akkoord, dat de binnendienst ziet op Akkoorden op offertes via de website. Deze aanroep schrijft ze niet naar de verkoopofferte, want het portaal leest verkoopdocumenten en schrijft er nooit in, en daardoor blijft dit koppelvlak klein. Ze bereiken het verkoopdocument langs twee wegen, die allebei door een persoon in Business Central worden uitgevoerd: automatisch als de binnendienst de offerte omzet in een order, waarbij het inkoopordernummer in Uw referentie en Extern documentnr. belandt en de datum in Verzochte leverdatum, of met de actie Op verkoopdocument zetten op de lijst met akkoorden. Geen van beide wegen overschrijft ooit een waarde die al op het document staat: wat de binnendienst heeft ingetypt, gaat voor op wat een week eerder op een website is gekozen.
RejectQuote
Legt met een reden vast dat de klant niet verdergaat met een vrijgegeven offerte. Alleen voor een Inkoper, met hetzelfde klantslot, dezelfde eis dat de offerte is vrijgegeven, en dezelfde uniforme weigering als AcceptQuote. De offerte wordt niet geannuleerd: er verandert niets in Business Central, de offerte blijft precies zoals ze was, en de binnendienst krijgt een regel in de werklijst dat de klant nee heeft gezegd. Wie "te duur" aanklikt, wil meestal een gesprek, en een portaal dat de offerte sluit, neemt dat gesprek weg.
| Parameter | Toelichting |
|---|---|
reasonCode | Een van TooExpensive, DeliveryTooLate, OrderedElsewhere, NoLongerNeeded, Other. Elke andere waarde, ook een lege tekenreeks, wordt opgeslagen als NotStated en laat de aanroep niet mislukken: de afwijzing zelf is de boodschap. Toon de lijst in uw eigen taal en stuur de vaste waarde mee. |
remark | Vrije tekst, optioneel, hooguit 250 tekens. Het is een persoonsgegeven en wordt na de bewaartermijn gewist; de reden en de offerte blijven staan. |
Het antwoord heeft outcome (recorded, already of refused), rejectedAt (een tijdstempel, of "") en, alleen bij refused, errorCode, dat altijd NotAuthorized is. Een ongeldige sessie, een andere rol, een leeg of te lang quoteNo, geen actuele vrijgegeven offerte van deze klant en een offerte waarvoor al een akkoord bestaat, geven allemaal refused: een akkoord intrekken is een gesprek, geen klik. Bestaat er al een afwijzing, dan is het antwoord already met de oorspronkelijke rejectedAt. Er is geen expired: een offerte afwijzen waarvan de geldigheidsdatum is verstreken, mag wel, en dat is juist het moment waarop een klant dat laat weten.
SwitchCustomer
Zet deze sessie over naar een andere klant waaraan het aangemelde adres is gekoppeld. Lees eerst Eén adres bij meerdere klanten hierboven: dit is de enige procedure in het portaal die een klantnummer accepteert. customerNo wordt getrimd en omgezet naar hoofdletters; een waarde langer dan 20 tekens wordt geweigerd, niet afgekapt. ipHash komt op de logregel van elke poging.
| Sleutel | Type | Toelichting |
|---|---|---|
outcome | string | switched of refused. |
customerNo | string | De klant waarop de sessie na de aanroep staat. "" bij refused: een weigering noemt nooit een klant. |
errorCode | string | Alleen bij refused. Altijd NotAuthorized. |
| Controle | Uitkomst |
|---|---|
| Sessie niet geldig | refused, en er wordt niets gelogd: zonder sessie is er niemand om te loggen |
customerNo is leeg of langer dan 20 tekens | refused |
| Sessie staat al op deze klant | switched, er wordt niets geschreven en niets gelogd |
| Geen koppeling tussen dit adres en die klant | refused |
| De koppeling bestaat wel, maar is ingetrokken of geanonimiseerd | refused |
| De klant is niet tot het portaal toegelaten | refused |
| Anders | switched; de klant en de rol van de sessie komen uit de koppeling |
Roep na een wissel ReadSession opnieuw aan, of gebruik de customerNo uit het antwoord: alles wat het portaal vanaf dat moment teruggeeft, is op de nieuwe klant gefilterd, inclusief de rol.
MyNotificationSettings
Waarover deze portaalgebruiker per e-mail bericht wil krijgen. Alles staat uit tot de gebruiker het zelf aanzet, en alleen dit endpoint kan het aanzetten: de binnendienst kan de vlaggen wel lezen maar niet zetten, en er is geen bulkactie.
| Sleutel | Type | Toelichting |
|---|---|---|
enabled | boolean | De hoofdschakelaar van het bedrijf (Portaalberichten ingeschakeld). Uit betekent dat er niets in de wachtrij komt en niets wordt verstuurd, wat de vijf hieronder ook zeggen. De waarde staat erbij zodat uw scherm kan uitleggen waarom er niets aankomt; een website kan haar niet wijzigen. |
quoteReleased | boolean | Er is een offerte voor deze klant vrijgegeven. |
orderConfirmed | boolean | Er is een order voor deze klant vrijgegeven. |
shipmentPosted | boolean | Er is een verzending voor deze klant geboekt. |
invoicePosted | boolean | Er is een factuur voor deze klant geboekt. |
requestReceived | boolean | Er is een offerteaanvraag van deze klant in het portaal binnengekomen. |
De vijf gelden per portaalgebruiker en per klant, want ze horen bij de koppeling. Wie voor twee bedrijven inkoopt, wil misschien verzendmail voor het ene en niets voor het andere. Elke mail vermeldt waarom hij is verstuurd en waar hij uit te zetten is, met een link naar het portaal, waar de ontvanger zich gewoon aanmeldt. Er is met opzet geen afmeldlink die zonder aanmelden werkt: zo'n link werkt voor iedereen die de e-mail in handen heeft, en dat is precies het soort endpoint dat deze app niet heeft.
SetNotificationSettings
Wijzigt de eigen keuzes van de aanroeper en geeft de nieuwe stand terug, in dezelfde vorm als MyNotificationSettings. De parameter settings is een JSON-object als tekenreeks, met de sleutels van MyNotificationSettings, bijvoorbeeld {"invoicePosted":true}.
- Alleen de sleutels die u stuurt, veranderen. Wat u weglaat, houdt zijn waarde, dus een scherm dat één vinkje omzet, hoeft de andere vier niet mee te sturen en kan een keuze uit een ander tabblad niet ongedaan maken.
enabledwordt genegeerd als u het meestuurt. Het is de schakelaar van het bedrijf; het staat in het antwoord zodat u iets kunt uitleggen, niet om het in te stellen.- Een waarde die geen
trueoffalseis, laat die sleutel ongemoeid. - Een
settingsdie geen geldige JSON is, wijzigt niets en geeft de huidige stand terug. Er is niets stuk: de klant klikte, er gebeurde niets, en het scherm toont wat er werkelijk staat. - Uitzetten werkt meteen, ook voor berichten die al in de wachtrij staan: op het moment van versturen wordt de wachtrij opnieuw aan deze vlaggen getoetst.
InviteColleague
Een Inkoper vraagt portaaltoegang aan voor een collega bij dezelfde klant. Dit geeft geen toegang: het maakt een gewone accountaanvraag waarin de klant al is ingevuld, en die keurt de binnendienst met de hand goed in Business Central, met dezelfde bevestiging als bij elke andere. Een inkoper die zijn eigen collega's binnen zou kunnen laten, zou toegang tot bedrijfsgegevens uitdelen, en dat is nu juist de enige handeling die deze app aan een mens overlaat.
| Parameter | Toelichting |
|---|---|
email | Het adres van de collega. Genormaliseerd zoals elk adres hier; er moet een @ in staan. |
name | De naam van de collega, hooguit 100 tekens. |
role | Buyer of Viewer, als voorstel. Elke andere waarde wordt Viewer. Wie goedkeurt, kiest de rol. |
privacyConsentVersion | Verplicht. De versie van de privacyverklaring die de uitnodiger op het uitnodigingsformulier heeft bevestigd. Deze aanroep legt het adres van een derde vast, en de uitnodiger staat daarvoor in; de waarde komt op de aanvraag te staan, naast het adres van de uitnodiger. Zonder deze waarde wordt de aanroep geweigerd. |
Het antwoord heeft outcome (created, already, throttled of refused), number (het nummer van de accountaanvraag bij created, anders "") en, alleen bij refused, errorCode, dat altijd NotAuthorized is. Een ongeldige sessie, een andere rol, Maximum aantal uitnodigingen per dag op 0, een email zonder @ en een lege privacyConsentVersion geven allemaal refused. Een adres dat bij deze klant al een actieve portaalgebruiker heeft, geeft already; een uitnodiger die vandaag zijn maximum heeft bereikt, krijgt throttled.
already en throttled staan met opzet apart, anders dan de uniforme weigeringen elders. Er valt hier niets te lekken: de aanroeper is aangemeld, heeft het adres zelf ingetypt, en already zegt alleen iets over de portaalgebruikers van zijn eigen klant, voor wie hij toch al mag optreden. Zo kan een scherm melden "deze collega kan zich al aanmelden" in plaats van "het is niet gelukt". Een ingetrokken koppeling geeft geen toegang, dus een collega die vertrok en terugkwam, kan opnieuw worden uitgenodigd. De collega krijgt de gewone ontvangstbevestiging van een accountaanvraag, met één extra regel die zegt wie hem heeft uitgenodigd, want "wij hebben uw aanvraag ontvangen" zegt niets aan iemand die niets heeft aangevraagd. Die mail gaat mee met de volgende mailronde.
MyDocuments
De pdf's die de klant van de sessie kan krijgen, als {"documents": [ … ]}, zonder truncated. De bron is een van twee, nooit beide: het documentarchief van Steel 365 als DXSteel Doc Archive is geïnstalleerd, anders de rapporten als Portaaldocumenten uit rapporten aan staat. Is het archief er niet en staat die instelling uit, dan is de lijst altijd leeg. Gaat er bij het lezen van het archief iets mis, dan krijgt u een lege lijst in plaats van een fout, en de pagina CustomerGate-instellingen toont waarom.
Archiefrijen. Een document staat alleen in de lijst als het aan het nummer van deze klant is gekoppeld, het relatietype Klant is en de rubriek op Archiefrubrieken staat met een andere soort dan Geen. De nieuwste eerst.
| Sleutel | Type | Toelichting |
|---|---|---|
id | string | SystemId van het archiefrecord, in kleine letters met koppeltekens. De sleutel om te downloaden. |
fileName | string | Zoals opgeslagen in het archief. Kan spaties en accenten bevatten; maak de naam schoon voordat u hem in een header zet. |
contentType | string | application/pdf bij .pdf, anders application/octet-stream. |
category | string | De soort: SalesQuote, SalesOrder, SalesInvoice, SalesCreditMemo, Proforma, Waybill of WarehouseShipment. Niet uw eigen rubrieknaam. |
documentNo | string | Het nummer van het brondocument, of "". |
documentType | string | quote, order, shipment of "". Pas ingevuld nadat Business Central heeft bevestigd dat documentNo zo'n document van deze klant is. Facturen, creditnota's en proforma's krijgen altijd "". |
documentNoStatus | string | Alleen als documentType order is: active of completed. |
date | string | De aanmaakdatum uit het archief, anders de datum waarop het record is aangemaakt, anders "". |
orderNo, orderNoStatus, invoiceNo, creditMemoNo | string | Optioneel. Nummers van bijbehorende documenten, met active, completed of "" voor de order. |
Een archiefdocument downloaden. Business Central kan de bytes niet via deze codeunit teruggeven zonder afhankelijk te worden van de app van iFacto, dus de website haalt ze met de id op bij de API van iFacto:
POST https://api.businesscentral.dynamics.com/v2.0/{tenant}/{environment}/api/ifacto/dxsteel/v1.0/companies({company id})/documentArchives({id})/Microsoft.NAV.Download
{}
-> {"value": "<base64>"}Download alleen een id die voorkomt in de lijst van MyDocuments die u net voor diezelfde sessie hebt opgehaald. Neem nooit een id uit de browser over om die ongecontroleerd door te geven: het account van de website heeft leesrecht op het archief nodig om te downloaden, en in hetzelfde archief staan ook inkoopfacturen en interne correspondentie. VOA CG S365 ARCHIVE beperkt die toegang met een beveiligingsfilter tot de toegestane rubrieken, maar de eigendomscontrole is MyDocuments. Een lege value betekent dat er geen bestand bij het archiefrecord zit.
Rapportrijen. Alleen documentsoorten waarvoor een rapport is ingesteld (een documentlay-out op de Klantenkaart, anders Rapportselecties): actuele offertes en orders, geboekte verzendingen, facturen en creditnota's, in die volgorde, elk met het hoogste nummer eerst en hooguit 100 per soort.
| Sleutel | Type | Toelichting |
|---|---|---|
id | string | SystemId van het document in Business Central. Ter informatie; downloaden doet u met Document. |
source | string | Altijd report. Ontbreekt bij archiefrijen: zo houdt u de twee uit elkaar. |
reportType | string | quote, order, shipment, invoice of creditMemo. Geef dit mee als documentType aan Document. |
fileName | string | {category} {document number}.pdf, bijvoorbeeld SalesInvoice 103001.pdf. |
contentType | string | application/pdf. |
category | string | SalesQuote, SalesOrder, Waybill (verzendingen), SalesInvoice of SalesCreditMemo. |
documentNo | string | Documentnummer. Geef dit mee als documentNo aan Document. |
documentType | string | quote, order of shipment; "" bij facturen en creditnota's. |
documentNoStatus | string | Alleen bij orders: active. |
date | string | Offerte: documentdatum. Order: orderdatum. Verzending, factuur, creditnota: boekingsdatum. |
orderNo, orderNoStatus, invoiceNo, creditMemoNo | string | Optioneel. Nummers van bijbehorende documenten. |
Document
De pdf van één document, afgedrukt met het rapport dat Business Central daarvoor gebruikt, en alleen bij de rapportbron: waar het archief van Steel 365 is geïnstalleerd, geeft dit altijd "" en gebruikt u de download uit het archief hierboven. documentType is quote, order, shipment, invoice of creditMemo (niet hoofdlettergevoelig) en documentNo is het nummer. De procedure geeft "" terug bij een ongeldige sessie, een onbekend type, een document dat niet bestaat of niet van deze klant is, een documentsoort zonder rapport, of een lege afdruk. Offertes en orders moeten actueel zijn; gearchiveerde versies kunnen niet worden afgedrukt. In alle andere gevallen komen fileName, contentType en base64 terug: de pdf zelf.
Het rapport wordt uitgevoerd met de rechten van het websiteaccount (tenant-set VOA CG DOCUMENTS). Mislukt het rapport, meestal doordat een klantspecifieke indeling een tabel leest die de set niet dekt, dan mislukt de aanroep met een fout die het object noemt; voeg die tabel toe aan de set. Een download archiveert de offerte of order niet, maar de standaardrapporten verhogen wel het veld Afgedrukt. hasDocument in MyQuotes, MyOrders en MyShipments is precies dan true als de rapportbron actief is en er voor die documentsoort een rapport voor deze klant is ingesteld.
MyAttachments en Attachment
Deze twee horen bij de bijlagen van een offerteaanvraag en zijn hierboven beschreven, onder Bijlagen bij een aanvraag. Ze gedragen zich als elke andere lezende procedure van het portaal: ze verlengen de sessie, staan open voor beide rollen en geven "" terug als er niets voor u is.
Webhooks: Business Central meldt zich bij u
Hierboven stelt steeds de website Business Central een vraag. Dit is de andere richting: Business Central roept een adres van u aan zodra er iets is gebeurd. Zo hoeft een website niet te pollen en niet op de nachtelijke synchronisatie te wachten om te horen dat er vanochtend een verzending de deur uit is gegaan.
Webhooks staan uit tot een beheerder in de Business Central-client een abonnement aanmaakt op de pagina Webhookabonnementen. Er is geen webservice die abonnementen aanmaakt of leest: het adres dat Business Central aanroept en het geheim waarmee het ondertekent, mag een website niet zelf kunnen instellen. Ook moet de beheerder de app in Extensiebeheer toestaan om uitgaande aanroepen te doen; zolang dat niet is gebeurd, komen gebeurtenissen wel in de wachtrij, maar wordt er niets verstuurd.
De gebeurtenissen
| Waarde van event | Wanneer hij optreedt | Waar hij vandaan komt |
|---|---|---|
QuoteReleased | Een verkoopofferte wordt vrijgegeven | de standaardroutine voor vrijgeven (niet in de voorbeeldstand) |
QuoteAccepted | Een klant accepteert een offerte in het portaal | AcceptQuote, op het moment dat het akkoord wordt vastgelegd |
OrderCreated | Er wordt een verkooporder uit een verkoopofferte gemaakt | de standaardomzetting van offerte naar order |
ShipmentPosted | Er wordt een verkoopverzending geboekt | de standaardboekingsroutine |
InvoicePosted | Er wordt een verkoopfactuur geboekt | de standaardboekingsroutine |
AccountApproved | Een accountaanvraag wordt goedgekeurd | de werklijst voor accountaanvragen |
AccountRejected | Een accountaanvraag wordt afgewezen | de werklijst voor accountaanvragen |
RequestConverted | Een offerteaanvraag wordt een verkoopofferte of een verkooporder | de status van de aanvraag, langs welke weg die ook is gewijzigd |
RequestRejected | Een offerteaanvraag wordt afgewezen | de status van de aanvraag, langs welke weg die ook is gewijzigd |
Ping | Alleen via Proefgebeurtenis versturen op de abonnementenlijst | nooit vanzelf |
Twee bewuste gaten:
- Een order via medewerker leidt niet tot
OrderCreated. Een aanvraag die rechtstreeks een verkooporder wordt, meldtRequestConvertedmetdocumentTyperequesten eenorderNo. Elke gebeurtenis heeft precies één bron, zodat een ontvanger nooit twee namen voor hetzelfde feit krijgt. - Spam is geen gebeurtenis. Een accountaanvraag die als spam is gemarkeerd, leidt tot geen enkele gebeurtenis: er is in het bedrijf niets gebeurd, en een antwoord erop bevestigt dat het adres bestaat.
- De opruimtaak anonimiseert oude offerteaanvragen die nog op Nieuw of In behandeling stonden, en zet ze daarbij op Afgewezen. Die leiden niet tot
RequestRejected; anders kwam er elke nacht een stortvloed aan gebeurtenissen binnen over aanvragen van een jaar geleden.
De aanroep
POST https://yoursite.example/api/bc-webhook
Content-Type: application/json; charset=utf-8
Accept: application/json
X-VOA-Timestamp: 2026-09-20T09:12:33.000Z
X-VOA-Signature: sha256=6687de2bb04779680fe96e12682e4a2b564bc4a3cc329edb2f551056de039e44
X-VOA-Event: InvoicePosted
X-VOA-Event-Id: 5b7a1f0c-2d43-4e18-9a55-0c7e3b6d81f2
X-VOA-Subscription: WEBSITE-ORDERS
{"event":"InvoicePosted", …}| Header | Betekenis |
|---|---|
X-VOA-Timestamp | Wanneer de bezorging is ondertekend, ISO 8601 in UTC. Onderdeel van de handtekening. |
X-VOA-Signature | sha256= gevolgd door 64 hexadecimale tekens in kleine letters. |
X-VOA-Event | Dezelfde waarde als event in de body, zodat een router niet eerst hoeft te ontleden. |
X-VOA-Event-Id | Dezelfde waarde als eventId in de body. Gebruik die om dubbele bezorgingen te herkennen. |
X-VOA-Subscription | De code van het abonnement, zodat één endpoint meerdere abonnementen kan bedienen. |
Antwoord met een 2xx-statuscode zodra u de gebeurtenis hebt opgeslagen, en doe het werk pas daarna. Elk ander antwoord dan 2xx telt als mislukt, net als een verbinding die niet binnen 10 seconden antwoordt. Het adres moet zelf antwoorden: bij een omleiding gaan de handtekeningheaders niet mee, dus een 301 naar een andere host komt daar zonder bruikbare handtekening aan.
De payload
De body is één JSON-object. Het bevat id's, statussen, bedragen en datums, maar geen persoonsgegevens: geen namen, geen e-mailadressen, geen telefoonnummers, geen adressen en geen vrije tekst. Dat is een regel van de app, geen toevalligheid van deze versie; een AL-toets faalt zodra er iets daarvan in een payload staat. Hebt u de naam achter een aanvraag nodig, vraag die dan op bij het portaal of bij Business Central, met het nummer dat u hier kreeg.
Elke payload bevat deze sleutels:
| Sleutel | Type | Betekenis |
|---|---|---|
eventId | tekst (GUID) | Wijst de gebeurtenis aan, niet de bezorging. Blijft gelijk bij nieuwe pogingen. Twee abonnementen op dezelfde gebeurtenis krijgen dezelfde eventId. |
event | tekst | Een van de waarden uit de tabel hierboven. |
occurredAt | tekst | Wanneer het in Business Central gebeurde, ISO 8601 in UTC. Niet het moment van bezorgen. |
company | tekst | De naam van het Business Central-bedrijf. |
documentType | tekst | quote, order, shipment, invoice, request, account of ping. |
documentNo | tekst | Optioneel. Het document waar de gebeurtenis over gaat. |
customerNo | tekst | Optioneel. De klant waar de gebeurtenis over gaat. |
En per gebeurtenis:
| Gebeurtenis | Extra sleutels |
|---|---|
QuoteReleased | status (Released), amountIncludingVat, currencyCode, documentDate, validUntil |
QuoteAccepted | status (Accepted), amountIncludingVat, currencyCode, documentDate, lineCount |
OrderCreated | quoteNo, amountIncludingVat, currencyCode, documentDate |
ShipmentPosted | orderNo (als de verzending uit een order komt), documentDate (de boekingsdatum) |
InvoicePosted | orderNo (als de factuur uit een order komt), amountIncludingVat, currencyCode, documentDate |
RequestConverted | status (Converted), requestNo en quoteNo of orderNo |
RequestRejected | status (Rejected), requestNo |
AccountApproved | status (Approved), requestNo |
AccountRejected | status (Rejected), requestNo |
Ping | geen |
amountIncludingVat is een JSON-getal in de valuta van het document; een lege currencyCode betekent de lokale valuta van het bedrijf. Datums zijn YYYY-MM-DD en een lege datum is "", zoals overal in deze naslag.
{
"event": "InvoicePosted",
"company": "Booischotse Metalen",
"documentType": "invoice",
"documentNo": "PS-INV101023",
"customerNo": "C00010",
"orderNo": "PS-ORD100411",
"amountIncludingVat": 4235.18,
"currencyCode": "",
"documentDate": "2026-09-20",
"eventId": "5b7a1f0c-2d43-4e18-9a55-0c7e3b6d81f2",
"occurredAt": "2026-09-20T09:12:31.117Z"
}De handtekening controleren
Een ontvanger die de handtekening niet kan controleren, vertrouwt iedereen die het adres kent. Het recept is kort, en er is geen excuus om het over te slaan. Elk abonnement heeft een eigen geheim van 64 hexadecimale tekens, dat u in Business Central aanmaakt met Geheim aanmaken en dat één keer wordt getoond. Zet het in de configuratie van uw server; daarna is het nooit meer te lezen: niet op de pagina, niet in een export en niet in een kopie van de database.
X-VOA-Signature = "sha256=" + lowercase_hex( HMAC_SHA256( secret, X-VOA-Timestamp + "." + raw_request_body ) )Drie details bepalen of het werkt:
- Over de ruwe body. Bereken de handtekening over de bytes zoals ze binnenkwamen, voordat u de JSON ontleedt en opnieuw serialiseert. Witruimte en de volgorde van de sleutels horen bij wat er is ondertekend.
- Het tijdstip zit in de handtekening, niet ernaast. Daarom loont het de moeite om het te controleren: anders kon iedereen die een bezorging onderschepte, die morgen opnieuw versturen met een nieuwe
X-VOA-Timestampen een handtekening die nog steeds klopte. - Vergelijk in constante tijd, en wijs een tijdstip af dat meer dan een paar minuten oud is of in de toekomst ligt.
import crypto from 'node:crypto'
// Express: express.raw({ type: 'application/json' }), dus de ruwe body, niet de ontlede.
export function handleWebhook (req, res) {
const secret = process.env.VOA_WEBHOOK_SECRET
const timestamp = req.get('X-VOA-Timestamp') || ''
const received = req.get('X-VOA-Signature') || ''
const rawBody = req.body // Buffer
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(timestamp + '.' + rawBody.toString('utf8'))
.digest('hex')
const a = Buffer.from(received)
const b = Buffer.from(expected)
if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
return res.status(401).end() // niet van Business Central
}
const age = Math.abs(Date.now() - Date.parse(timestamp))
if (!Number.isFinite(age) || age > 5 * 60 * 1000) {
return res.status(401).end() // herhaling, of een klok die ver afwijkt
}
const event = JSON.parse(rawBody.toString('utf8'))
if (alreadyHandled(event.eventId)) return res.status(200).end() // een nieuwe poging
store(event) // doe het werk waar mogelijk pas na het antwoord
res.status(200).end()
}Met deze proefwaarden toetst u uw implementatie zonder Business Central. Het zijn dezelfde waarden die de AL-toets gebruikt:
| Wat | Waarde |
|---|---|
| geheim | voa-test-secret |
X-VOA-Timestamp | 2026-09-20T09:12:33.000Z |
| body | {"event":"InvoicePosted","documentNo":"PS-INV101023"} |
| handtekening | sha256=6687de2bb04779680fe96e12682e4a2b564bc4a3cc329edb2f551056de039e44 |
Eén seconde later, met 2026-09-20T09:12:34.000Z over dezelfde body, is het resultaat sha256=60bd375378f87dd5a30241d3c524a6a38d5f036222756e2727642488b73e8e92. Geeft uw implementatie voor beide tijdstippen hetzelfde antwoord, dan ondertekent u het tijdstip niet.
Nieuwe pogingen, dubbele bezorgingen en volgorde
- Een bezorging wordt vijf keer geprobeerd, met 1, 5, 30 en 120 minuten ertussen. Daarna wordt ze geparkeerd en komt ze op de lijst Webhookbezorgingen in Business Central te staan, waar iemand haar met Nu opnieuw proberen terug in de wachtrij kan zetten.
- Bezorgingen gaan via een wachtrij en worden nooit vanuit een boeking verstuurd. Een gebeurtenis die ontstaat terwijl er een factuur wordt geboekt, wordt in diezelfde transactie naar een wachtrijtabel geschreven, en een taakwachtrijpost doet de aanroep. Een traag of kapot endpoint kan een boeking dus nooit ophouden en nooit laten terugdraaien. Wordt de boeking teruggedraaid, dan verdwijnt de klaargezette gebeurtenis ook.
- Reken op dubbele bezorgingen. Een bezorging die aan uw kant een time-out kreeg, kan best zijn aangekomen. Ontdubbel op
eventId; die blijft bij nieuwe pogingen gelijk. - Reken niet op een vaste volgorde. Twee gebeurtenissen binnen dezelfde minuut kunnen in willekeurige volgorde aankomen, en een gebeurtenis die opnieuw wordt bezorgd, komt pas binnen na gebeurtenissen die later plaatsvonden. Gebruik
occurredAtals de volgorde ertoe doet. - Na twintig mislukte pogingen op rij (instelbaar op CustomerGate-instellingen) zet een abonnement zichzelf uit, en het gaat nooit vanzelf weer aan. Iemand controleert het endpoint en kiest Aanzetten. Een omgeving die uitgaande HTTP-aanroepen blokkeert, telt niet als mislukking van het endpoint: dat is een instelling, geen kapotte ontvanger.
Antwoord met 2xx op een event dat u niet kent. In een compatibele release kunnen er waarden bij komen, en een ontvanger die een onbekende naam met een fout beantwoordt, zet het abonnement uit vanwege een gebeurtenis die hem niet eens aangaat.
voaPing: wat voor installatie dit is
Eén actie, POST .../ODataV4/voaPing_Ping?company={company id} met een lege body, die de vragen beantwoordt die u anders met proefaanroepen zou moeten uitzoeken: is dit productie of een sandbox, is Steel 365 geïnstalleerd (en zijn er dus drie aantallen per regel in plaats van één), waar komen de pdf's vandaan, is de inrichting af, en is dit een demobedrijf waarvan de mail nergens aankomt.
Er is geen sessietoken voor nodig en er komen geen persoonsgegevens terug: geen klantnaam, geen e-mailadres, geen aantallen. Roep de actie één keer aan als uw toepassing start, en nog eens na een uitrol. Een aanroep is goedkoop, maar dit is ook het enige endpoint dat een aanroeper zonder sessie iets vertelt, dus poll het niet.
{
"app": {"name": "CustomerGate 365", "publisher": "Van Oosten Software", "version": "1.0.39.0"},
"contractVersion": "1.0",
"environment": {"type": "Production", "companyName": "CRONUS BE"},
"modules": {"steel365": false, "steel365Archive": false},
"documentSource": "report",
"demoMode": false,
"setupComplete": true,
"supportedLocales": ["en-US", "nl-NL", "nl-BE", "fr-BE", "fr-FR"],
"serverTimeUtc": "2026-09-20T11:24:07.421Z",
"licence": {
"status": "Active",
"plan": "Portal",
"validUntil": "2027-12-31",
"servicesStopOn": "2028-01-31",
"features": {"requests": true, "catalog": true, "portal": true, "attachments": true, "webhooks": false}
},
"attachments": {
"enabled": true,
"maxFileSizeKb": 10240,
"maxPerRequest": 10,
"allowedTypes": ["pdf", "png", "jpg", "jpeg", "dxf", "dwg", "step", "stp", "xlsx", "csv"],
"deniedTypes": ["exe", "dll", "js", "vbs", "ps1", "bat", "cmd", "scr", "msi", "jar", "sh", "html", "htm", "svg"],
"contentCheckedTypes": ["pdf", "png", "jpg", "jpeg", "xlsx", "dwg", "step", "stp"]
}
}| Sleutel | Type | Toelichting |
|---|---|---|
app.name, app.publisher | tekst | De naam en de uitgever van de app, uit de module zelf. |
app.version | tekst | De appversie, vier getallen. Verandert bij elke build. |
contractVersion | tekst | De versie van dit koppelvlak, niet van de app. Verandert alleen als de afspraak verandert. |
environment.type | tekst | Production of Sandbox. |
environment.companyName | tekst | De naam van het Business Central-bedrijf waarin de aanroep liep. Een codeunitservice wordt met ?company={guid} aangeroepen, dus dit is de enige manier om te zien welk bedrijf dat was. Het is geen klantnaam. |
modules.steel365 | boolean | iFacto Steel 365 (DXSteel) is geïnstalleerd. Bepaalt de vorm van DocumentLines. |
modules.steel365Archive | boolean | DXSteel Doc Archive is geïnstalleerd. |
documentSource | tekst | archive, report of none. Waar de pdf's in het portaal vandaan komen; zie de tabel hieronder. |
demoMode | boolean | De demostand staat aan: elk bericht van CustomerGate gaat naar één demoadres in plaats van naar de klant. |
setupComplete | boolean | false zolang het instellingenrecord ontbreekt, een nummerreeks leeg is of de client-id van de website niet is ingevuld. false betekent dat aanroepen mislukken; meld dat op uw eigen statuspagina in plaats van te gissen naar de oorzaak van een 403. |
supportedLocales | array van tekst | De talen waarin de app is vertaald. Gebruik een daarvan in Accept-Language. |
serverTimeUtc | tekst | De klok van de server, ISO 8601 in UTC. Handig om een klokverschil op te merken voordat het tot een verlopen sessie leidt. |
licence.status | tekst | De huidige stand van de licentie in dit bedrijf: NotRequired, Demo, Trial, Active, Expiring, Grace, EnvironmentChanged, Lapsed of CompanyNotCovered. Alleen de laatste twee leggen de webservices stil. |
licence.plan | tekst | Requests, Portal of Complete; zonder geldige sleutel None. Complete in een sandbox, in de demostand en tijdens de proefperiode. |
licence.validUntil | tekst | De laatste dag van de licentie (yyyy-mm-dd), of leeg als er geen sleutel is. |
licence.servicesStopOn | tekst | De eerste dag waarop de webservices stoppen (of stopten) als er niets verandert, of leeg als dat niet in zicht is. |
licence.features | object | Per functie of een aanroep nu zou lukken: requests, catalog, portal, attachments, webhooks. |
attachments.enabled | boolean | Sinds 1.0.44. Of AddAttachment nu werkt: Bijlagen ingeschakeld staat aan in dit bedrijf en de licentie dekt bijlagen (licence.features.attachments). false betekent: toon geen uploadveld. |
attachments.maxFileSizeKb | getal | Sinds 1.0.44. Het grootste bestand dat AddAttachment accepteert, in kilobytes gedecodeerde inhoud, met de terugvalwaarde toegepast: 0 in de instellingen is hier 10240. |
attachments.maxPerRequest | getal | Sinds 1.0.44. Hoeveel bestanden één aanvraag mag hebben. 0 in de instellingen is hier 10. |
attachments.allowedTypes | array van tekst | Sinds 1.0.44. De extensies die AddAttachment accepteert, in kleine letters, zonder punt, elk één keer, precies zoals de controle bij het uploaden ze leest. Een waarde in Toegestane soorten bijlagen die op de weigerlijst staat, een punt bevat (.pdf) of langer is dan tien tekens, kan nooit een bestand doorlaten en valt weg. Een lege instelling geeft de standaardlijst. |
attachments.deniedTypes | array van tekst | Sinds 1.0.44. De vaste weigerlijst uit de code. Geweigerd op elk deel van een bestandsnaam, wat de instellingen ook zeggen (drawing.exe.pdf wordt geweigerd). |
attachments.contentCheckedTypes | array van tekst | Sinds 1.0.44. De extensies waarvan de eerste bytes bij de extensie moeten passen. Een soort die is toegestaan maar niet in deze lijst staat (dxf, csv), wordt op zijn naam geaccepteerd. |
documentSource verdient een eigen toelichting, want die bepaalt welk van twee downloadpaden uw site moet bouwen:
| Waarde | Betekenis | Wat u bouwt |
|---|---|---|
archive | DXSteel Doc Archive is geïnstalleerd. | MyDocuments geeft archiefrijen terug; haal de bytes met de id op bij de eigen API van iFacto. Document geeft altijd "" terug. |
report | Geen archief, en Portaaldocumenten uit rapporten staat aan. | MyDocuments geeft rapportrijen terug; download via Document. |
none | Geen archief en de instelling staat uit. | Helemaal geen documenten. MyDocuments is altijd leeg en Document geeft altijd "" terug; toon geen downloadknop. |
Zoals bij elke codeunitservice is het antwoord een tekenreeks binnen value: ontleed die een tweede keer. voaPing heeft de machtigingenset VOA WEB CONTRACT nodig; Machtigingen herstellen wijst die samen met de andere drie toe aan de Entra-toepassing van de website. Een omgeving die al was ingericht voordat deze service bestond, krijgt de service bij de volgende update van de app en de set bij de volgende keer dat Machtigingen herstellen wordt uitgevoerd; tot die tijd antwoordt de aanroep met 403.
voaPing stopt nooit vanwege de licentie: juist hiermee komt een website te weten waarom de rest stopte. Welke licentiefunctie elke aanroep nodig heeft, staat per procedure en per pagina als licence in het contractbestand.
Foutcodes
Een zin is geen houvast voor uw code. De zin wordt vertaald, want de aanroep stuurt een Accept-Language mee, en hij wordt herschreven zodra iemand hem duidelijker formuleert: twee manieren om uw foutafhandeling kwijt te raken zonder dat er iets op rood springt.
Elke fout die een client kan veroorzaken, heeft daarom een machineleesbaar merkteken aan het begin van de melding:
[VOA-PrivacyConsentMissing] A request cannot be accepted without agreement to the privacy statement. Send privacyAccepted = true.- Het merkteken bestaat uit
[VOA-, de code en], staat aan het begin van de melding en wordt nooit vertaald. - De melding erachter is ongewijzigd en blijft in de taal van de sessie.
- Laat uw code afhangen van het merkteken, nooit van de zin.
- Het oudere merkteken
[VOA-DUBBEL]staat nog steeds in de melding over een dubbelerequestId, waar het altijd stond. Het is niet vervangen, zodat een website die de codes pas later leert kennen, niet stukgaat.
De codes
| Code | Waar | Wat er gebeurde | Wat een client moet doen |
|---|---|---|---|
RequestsClosed | CreateQuoteRequest, CreateAccountRequest | Aanvragen weigeren staat aan in de instellingen: CustomerGate neemt tijdelijk niets aan. | Toon dat versturen tijdelijk niet mogelijk is. Dit is de enige code waarbij u het later opnieuw mag proberen. Verstuur niet automatisch opnieuw. |
PrivacyConsentMissing | CreateQuoteRequest, CreateAccountRequest | privacyAccepted was niet true, of privacyConsentVersion was leeg. | Een fout in het formulier. Toon het selectievakje voor de toestemming; probeer het niet opnieuw. |
InvalidInput | CreateQuoteRequest, CreateAccountRequest | De payload is geen geldige JSON, een verplichte waarde ontbreekt (email bij een accountaanvraag), of een waarde is niet te lezen (requestedDeliveryDate is geen datum). De melding noemt de eigenschap. | Herstel de invoer. Probeer het niet opnieuw met dezelfde payload. |
DuplicateRequest | CreateQuoteRequest, CreateAccountRequest | Deze requestId is al aangenomen. De melding bevat ook [VOA-DUBBEL] en het nummer van de opgeslagen aanvraag. | Beschouw het als geslaagd. De aanvraag staat in Business Central. |
SetupIncomplete | CreateQuoteRequest, CreateAccountRequest | CustomerGate is niet ingericht in dit bedrijf: er is geen nummerreeks voor de aanvragen. | Niet het probleem van de bezoeker: waarschuw wie de omgeving beheert. voaPing meldt hetzelfde als setupComplete: false. |
ItemNotOnWebsite | CreateQuoteRequest | Een regel verwijst naar een artikel dat niet in de webcatalogus staat. | Een fout of een verouderde pagina: de bezoeker kijkt naar iets wat niet online te koop is. Ververs de catalogus en laat de regel weg. |
ItemNotQuotable | CreateQuoteRequest | Een regel verwijst naar een artikel dat wel in de catalogus staat, maar niet online bestelbaar is (webQuotable is false). | Toon voor dat artikel "prijs op aanvraag" in plaats van een bestelknop, en bied het contactformulier aan. |
CatalogNotComputed | voaItems, voaVariants, voaCategories, voaCatalogMedia | De webcatalogus is in dit bedrijf nog nooit berekend, dus Business Central kan niet zeggen welke artikelen op de website horen. | Stop de synchronisatie en bewaar wat u al hebt. Vat dit niet op als een lege catalogus. Wie de omgeving beheert, kiest Webcatalogus herberekenen op CustomerGate-instellingen. |
LicenceRequired | Elke service behalve voaPing en SignOut | Er is geen geldige licentie voor deze omgeving (na de proef- of respijtperiode), of de licentie geldt niet voor dit bedrijf. Er is niets gelezen of geschreven. | Toon dat de functie tijdelijk niet beschikbaar is en bewaar wat u al hebt. Niet het probleem van de bezoeker: voaPing vertelt waarom en sinds wanneer. Opnieuw proberen heeft geen zin zolang er geen sleutel is. |
NotInPlan | voaPortal en AddAttachment bij het pakket Requests. In een lager pakket dan Complete geven webhooks geen foutcode: er komt niets in de wachtrij en er wordt niets verstuurd. | Het pakket van deze omgeving omvat deze functie niet, en dat blijft zo tot het pakket verandert. | Verberg de functie. voaPing noemt in licence.features wat werkt. |
NotAuthorized | AcceptQuote, AcceptQuoteWithDetails, RejectQuote, SwitchCustomer, InviteColleague (als errorCode) | De aanroep is geweigerd. Altijd dezelfde code, om welke reden dan ook, op alle vijf de plekken. | Toon dat het niet kan. Zie hieronder. |
QuoteNotOpen | AcceptQuote, AcceptQuoteWithDetails (als errorCode) | De geldigheidsdatum van de offerte is verstreken. | Toon dat de offerte verlopen is en bied aan om een nieuwe aan te vragen. |
StaffOrdersDisabled | CreateQuoteRequest | source is staffOrder, maar Orders via medewerker ingeschakeld staat uit op CustomerGate-instellingen. Er is niets opgeslagen. | Geen probleem van de bezoeker: wie de omgeving beheert zet de instelling aan, of orders via medewerker zijn niet voor dit bedrijf bedoeld. Verstuur hem niet opnieuw als gewone aanvraag. |
errorCode bij AcceptQuote en verwante procedures
AcceptQuote werpt geen fout op, maar antwoordt met een object. Alleen als de uitkomst een mislukking is, bevat dat object naast de sleutels die het al had ook een errorCode.
| Uitkomst | errorCode | Toelichting |
|---|---|---|
recorded | afwezig | Het akkoord is binnen. |
already | afwezig | Er was al een akkoord; geen fout. |
expired | QuoteNotOpen | De offerte is aantoonbaar van deze klant, en de geldigheidsdatum is verstreken. |
refused | NotAuthorized | Altijd dezelfde code, om welke reden dan ook. |
Die laatste regel is een bewuste keuze en verandert niet. Een offerte van een andere klant, een nummer dat niet bestaat, een offerte die niet is vrijgegeven en een Meekijker die niet mag accepteren, leveren allemaal hetzelfde antwoord op, want elk ander antwoord maakt van dit endpoint een gratis manier om na te gaan wat er bestaat en van wie het is. De code geeft u een stabiel houvast, geen reden. RejectQuote, SwitchCustomer en InviteColleague volgen hetzelfde patroon: errorCode verschijnt alleen bij refused en is dan altijd NotAuthorized. De uitkomsten already en throttled zijn geen fouten en hebben geen code.
Codes die bestaan maar nooit worden teruggegeven
De lijst met codes bevat ook SessionExpired, SessionUnknown, SignInCodeInvalid, SignInCodeExpired, SignInCodeThrottled, AccountNotActive, AccountUnknown, DocumentNotFound, DocumentNotAllowed, QuoteChanged, RequestNotFound, ModuleNotAvailable en Internal. Het portaal geeft ze op dit moment nooit terug, en dat is niet over het hoofd gezien: voaPortal beantwoordt een ongeldige sessie, een document van een andere klant en een document dat niet bestaat bewust met precies hetzelfde lege antwoord. De namen zijn gepubliceerd zodat ze vanaf het begin bij het koppelvlak horen, en zodat een latere versie er een kan gebruiken zonder de lijst te hoeven uitbreiden. In het contractbestand zijn ze gemarkeerd met "reserved": true.
Het contract als bestand en de client
Alles hierboven is proza. Een generator heeft gegevens nodig, dus hetzelfde koppelvlak wordt ook als bestand gepubliceerd: contract/customergate-365.contract.json. Het wordt uit de AL-broncode gegenereerd, met de app meegeleverd en bij elke pull request opnieuw gegenereerd en vergeleken, zodat een wijziging aan het koppelvlak als verschil zichtbaar wordt en nooit ongemerkt gebeurt.
| Sleutel | Inhoud |
|---|---|
contractVersion | Dezelfde waarde die voaPing teruggeeft. |
app | Id, naam, uitgever, versie en de ondersteunde talen. |
services[] | Elke gepubliceerde service, met objectsoort en objectnummer. |
services[].procedures[] | Bij een codeunitservice: elke aanroepbare procedure, de naam van de OData-actie (voaPortal_MyQuotes), de parameters met hun AL-typen, het retourtype, de JSON-sleutels die ze schrijft (jsonKeys) en de sleutels die ze uit de payload leest (payloadKeys, bij voaRequests). |
services[].fixedValues[] | Per JSON-sleutel de vaste waarden die erin kunnen staan, met de naam van de AL-helper waar ze vandaan komen. status staat er twee keer in, omdat de status van een aanvraag en die van een document verschillende lijsten zijn. |
services[].properties[] | Bij een paginaservice: de OData-eigenschapsnamen. |
enums[] | Elke enum van de app met de namen van de waarden: dezelfde als onder Vaste waarden hieronder. |
errorCodes[] | Elke code uit Foutcodes, met het merkteken, de plek waar hij wordt opgeworpen en of hij gereserveerd is. |
Wat er met opzet niet in staat: welke sleutel optioneel is, welke sleutel alleen met Steel 365 verschijnt, wat een waarde betekent en welke procedure alleen leest. Dat is redactionele inhoud en staat op deze pagina. Gebruik het bestand voor de namen en de structuur, en deze pagina voor de regels.
De gegenereerde TypeScript-client
Bouwt u in TypeScript op deze app, dan hoeft u het technische werk van deze pagina niet zelf te schrijven. Er is een clientpakket dat uit het contractbestand wordt gegenereerd, dus het kan geen service beschrijven die niet bestaat en geen service overslaan die wel bestaat.
const cg = createClient({ tenant, environment, company: { id, name }, getToken });
const session = await cg.voaPortal.readSession({ token }); // al twee keer ontleedWat het u uit handen neemt:
- De OData-envelop en het tweede ontleden, per procedure afgeleid uit
returnsenjsonKeysin het contract: een tekstantwoord met JSON-sleutels wordt ontleed, een sessietoken niet. - Het lege antwoord:
""komt binnen alsnull, wat de reden ook is. - De parameternamen, die vóór de aanroep worden gecontroleerd, omdat OData op naam bindt en een verkeerde naam een kale 400 oplevert.
[VOA-…]als een getypeerdecodeop een foutobject, met een aparte vlag voor een dubbele verzending, inclusief het oudere[VOA-DUBBEL].- De regels voor opnieuw proberen: altijd na een 429, één keer na een geweigerd token, en verder nooit, tenzij u zelf een procedure als lezend aanmerkt.
- De vaste waarden en de waardenamen van de enums, als union-typen.
- Pagineren via
@odata.nextLinkbij de catalogusservices.
Het pakket heeft geen runtime-afhankelijkheden, leest nooit een omgevingsvariabele en bewaart nooit een geheim: u geeft het een functie mee die een token teruggeeft. Het versienummer volgt contractVersion en niet de versie van de app; 1.0.x hoort bij contract 1.0, en het derde getal is voor correcties in het pakket zelf. De generator weigert te draaien als die twee niet overeenkomen.
Het contractbestand en het clientpakket krijgt u van Van Oosten Software; het pakket staat nog niet op npm, dus u installeert het uit de broncode die u hebt gekregen. Controleer bij het opstarten waarmee u praat, door contractVersion uit voaPing te vergelijken met de versie waarvoor uw client is gebouwd.
Vaste waarden
Deze waarden liggen vast. Ze worden nooit vertaald en hangen nooit af van de taal van de sessie. Vergelijk ze exact.
| Waar | Sleutel of parameter | Waarden |
|---|---|---|
ReadSession, MyDetails | role | Buyer, Viewer |
ReadSession, MyDetails | customers[].role | Buyer, Viewer |
MyRequests | status | New, InProgress, Converted, Rejected, "" |
MyRequests | documentType | quote, order |
MyQuotes, MyOrders | status | Open, Released, "" |
MyRequests, MyOrders | quoteNoStatus | active, completed, "" |
MyQuotes, MyShipments, MyDocuments | orderNoStatus | active, completed, "" |
MyDocuments | documentNoStatus | active, completed |
DocumentLines | lineStatus | Open, PartlyShipped, Shipped, Invoiced |
AcceptQuote, AcceptQuoteWithDetails | outcome | recorded, already, expired, refused |
RejectQuote | outcome | recorded, already, refused |
RejectQuote | parameter reasonCode | TooExpensive, DeliveryTooLate, OrderedElsewhere, NoLongerNeeded, Other (elke andere waarde wordt opgeslagen als NotStated) |
MyQuotes | rejectionReason | TooExpensive, DeliveryTooLate, OrderedElsewhere, NoLongerNeeded, Other, NotStated, "" (sinds 1.0.44) |
CreateQuoteRequest, RequestLines | regel inspectionCertificate | None, Certificate22, Certificate31, Certificate32 (bij invoer wordt elke andere waarde None) (sinds 1.0.44) |
CreateQuoteRequest, RequestLines | regel processing (array) | CutToLength, MitreCutting, Drilling, Punching, ThermalCutting, Bending, Blasting, Priming, Galvanizing, Painting (bij invoer wordt elke andere waarde overgeslagen) (sinds 1.0.44) |
SwitchCustomer | outcome | switched, refused |
InviteColleague | outcome | created, already, throttled, refused |
InviteColleague | parameter role | Buyer, Viewer (elke andere waarde: Viewer) |
MyNotificationSettings, SetNotificationSettings | sleutels | enabled, quoteReleased, orderConfirmed, shipmentPosted, invoicePosted, requestReceived |
DocumentLines | parameter documentType | quote, order, shipment |
Document | parameter documentType | quote, order, shipment, invoice, creditMemo |
MyDocuments | documentType | quote, order, shipment, "" |
MyDocuments | category | SalesQuote, SalesOrder, SalesInvoice, SalesCreditMemo, Proforma, Waybill, WarehouseShipment |
MyDocuments | source | report (alleen rapportrijen) |
MyDocuments | reportType | quote, order, shipment, invoice, creditMemo |
MyDocuments, Document | contentType | application/pdf, application/octet-stream |
MyAttachments | origin | PortalUser, InsideSales |
MyAttachments, Attachment | contentType | Afgeleid uit de extensie: application/pdf, image/png, image/jpeg, image/vnd.dxf, image/vnd.dwg, model/step, text/csv, de Office-typen, anders application/octet-stream |
AddAttachment | weigerlijst | exe, dll, js, vbs, ps1, bat, cmd, scr, msi, jar, sh, html, htm, svg: ligt vast in de code en kan door geen instelling worden overschreven; voaPing geeft die terug als attachments.deniedTypes (sinds 1.0.44) |
ReadSession, DocumentLines | weightType | Theoretical, Trade, GermanTrade, "" (Steel 365) |
DocumentLines | unit | Onvertaalde optienamen van het Steel 365-veld; de referentiewebsite verwerkt PCS, KG, MTR |
voaRemovals | Kind | Item, Variant, Category |
voaCatalogMedia | contentType | Wat Business Central heeft opgeslagen, bijvoorbeeld image/jpeg of image/png; application/octet-stream als dat onbekend is |
CreateQuoteRequest | deliveryMethod | Pickup, Delivery (elke andere waarde: Pickup) |
CreateQuoteRequest | source | Vrije tekst tot 50 tekens. staffOrder maakt er een order via medewerker van (alleen als Orders via medewerker ingeschakeld aanstaat); LinkRequest zet portal. |
CreateAccountRequest | existingCustomer | Yes, No, Unknown (elke andere waarde: Unknown) |
| Webhook | event | Ping, QuoteReleased, QuoteAccepted, OrderCreated, ShipmentPosted, InvoicePosted, AccountApproved, AccountRejected, RequestConverted, RequestRejected |
| Webhook | documentType | quote, order, shipment, invoice, request, account, ping |
| Webhook | X-VOA-Signature | sha256= plus 64 hexadecimale tekens in kleine letters |
Ping | environment.type | Production, Sandbox |
Ping | documentSource | archive, report, none |
Ping | contractVersion | 1.0 |
Fouten van voaRequests | melding | [VOA-DUBBEL] markeert een dubbele requestId |
| Fouten van de catalogus | melding | [VOA-CatalogNotComputed], [VOA-ItemNotOnWebsite], [VOA-ItemNotQuotable] |
| Elke fout die een client kan veroorzaken | melding | Begint met [VOA-<code>]; zie Foutcodes |
| Aanmeldcode | alfabet | 0123456789ABCDEFGHJKMNPQRSTVWXYZ, 10 tekens |
Aanvraagnummers komen uit nummerreeksen in de instellingen. Bij installatie staan die standaard op WEB000001 tot WEB999999 voor offerteaanvragen en ACC000001 tot ACC999999 voor accountaanvragen, maar elk bedrijf kan ze wijzigen: ga niet uit van dit patroon.
Verschillen met Steel 365
CustomerGate 365 is één app. Die controleert zelf de omgeving en heeft daar geen instelling voor nodig: Steel 365 is actief zodra het veld Quantity Pieces DMX op de verkoopregel bestaat, en het documentarchief van Steel 365 zodra de tabel Document Archive DADMX bestaat.
| Onderdeel | Met Steel 365 | Zonder Steel 365 |
|---|---|---|
| Catalogusservices | voaProfiles, voaGrades en voaItemTypes zijn gepubliceerd, en de DX…-eigenschappen zijn gevuld | Alleen voaItems, voaVariants, voaCategories, voaRemovals en voaCatalogMedia; DX…-tekst is "" en getallen zijn 0 |
| Machtigingen voor synchronisatie | VOA WEB SYNC en VOA CG S365 SYNC | Alleen VOA WEB SYNC |
Aantallen in DocumentLines | quantityPieces, quantityMeters, quantityKg plus unit; quantity komt nooit voor | quantity plus unitOfMeasureCode |
DocumentLines bij actuele orderregels | outstandingPieces/Meters/Kg, shippedPieces/Meters/Kg | outstandingQuantity, quantityShipped |
Extra sleutels in DocumentLines | profile, grade, formatDescription, weightType als ze gevuld zijn; sinds 1.0.44 lengthMm, widthMm, thicknessMm en customFormat uit de variant van de regel | geen |
requestedDeliveryDate, promisedDeliveryDate en lineStatus per regel | aanwezig bij actuele orderregels; lineStatus komt uit de standaardaantallen | hetzelfde: deze drie zijn niet van Steel 365 afhankelijk |
MyQuotes.totalWeightKg | aanwezig | afwezig |
ReadSession.weightType | het gewichtstype van de klant, of "" | "" |
| Geldigheid van een offerte | valt terug op de vervaldatum uit Steel 365 | alleen de geldigheidsdatum van de offerte |
Het documentarchief is een aparte schakelaar:
| Onderdeel | Met het archief | Zonder archief |
|---|---|---|
MyDocuments | Archiefrijen, gefilterd op de toegestane rubrieken | Rapportrijen als Portaaldocumenten uit rapporten aanstaat, anders leeg |
| Pdf downloaden | De bound action van de iFacto-API met de id | Document geeft base64 terug |
Document | altijd "" | drukt de pdf af |
hasDocument | altijd false | true als er een rapport is ingesteld |
| Offertes en orders die alleen nog in het archief staan | orderNo- en quoteNo-verwijzingen uit het archief | geen verwijzingen |
| Tenant-set van de website | VOA CG S365 ARCHIVE | VOA CG DOCUMENTS als de instelling aanstaat |
Ping.documentSource | archive | report, of none als de instelling uitstaat |
Versies en compatibiliteit
De namen zijn het contract. Websites gebruiken ze letterlijk, dus dit hoort er allemaal bij: de namen van de services, de namen van procedures en parameters inclusief hoofdlettergebruik, de namen van de cataloguseigenschappen, de JSON-sleutels in payloads en antwoorden, elke vaste waarde hierboven, de foutcodes en de vorm [VOA-…] van het merkteken. Wie er één van hernoemt, maakt elke website die op de app is gebouwd stuk, en vaak zonder dat iemand het merkt: een hernoemde cataloguseigenschap laat gewoon een kolom leeg, en een hernoemde JSON-sleutel wordt gelezen als null. Er zijn geen aliassen voor eerdere namen.
Voor de webhooks gelden dezelfde regels: de namen van de headers, de sleutels in de payload, de waarden van event en het recept voor de handtekening horen bij het koppelvlak. Een nieuwe waarde van event vraagt om een wijziging aan de ontvangende kant voordat klanten hem zien, en een ontvanger die een onbekende naam niet met 2xx beantwoordt, zet zijn eigen abonnement uit vanwege iets wat hem niet aangaat.
Voor wie de services gebruikt:
- Negeer sleutels die u niet kent. In een compatibele release kunnen er sleutels bij komen.
- Behandel een ontbrekende optionele sleutel als null, en
""als geen waarde. - Behandel een onbekende waarde als onbekend. Zet haar niet om naar een standaardwaarde die iets beweert, bijvoorbeeld door een onbekende
outcomealsrecordedte lezen. - De waarden van
inspectionCertificateenprocessing(sinds 1.0.44) kunnen groeien: een andere app kan er een toevoegen, en die komt dan onder zijn eigen naam terug uitRequestLines. Behandel een naam die u niet kent als onbekend. - Houd het sessietoken, de
requestId, het webhookgeheim en de sleutel voor de IP-hash aan de serverkant.
Een sleutel, een procedure of een cataloguseigenschap toevoegen is een compatibele wijziging; er een verwijderen of hernoemen niet. Een parameter toevoegen aan een bestaande actie evenmin, omdat OData op naam bindt: daarom staat AcceptQuoteWithDetails naast AcceptQuote in plaats van erin. Installaties en updates maken ontbrekende services aan, maar wijzigen nooit een bestaande: een hernoemde service zou naast de oude komen te staan, terwijl de oude blijft verwijzen naar hetzelfde als voorheen.
contractVersion is niet de appversie. De app krijgt bij elke build een nieuw versienummer van vier getallen, terwijl contractVersion alleen verandert als er aan deze afspraak iets verandert dat niet compatibel is. Controleer bij het opstarten dus contractVersion uit voaPing, niet app.version.
Verder in de documentatie
- Installatiegids
Van het installeren tot een eerste aanvraag, en verder: de taakwachtrijposten, de webcatalogus, webhooks en de diagnose.
- Versiegeschiedenis
Wat er in elke versie van de app zit.
- Ondersteuning
Hoe u hulp vraagt, en wat u meestuurt.