Sign inGet Started

E-mailtemplates

Een template is een onderwerpregel en een e-mailbody die je één keer opslaat en vele keren verstuurt. Je schrijft de veranderlijke delen als {{ variable }}-placeholders, publiceert het template, en verstuurt het vervolgens op slug in plaats van dezelfde HTML in elke API-aanroep te plakken. Een template hoort bij je werkruimte.

Maak en beheer templates in Email > Templates, via /v1/email/templates, met de bird CLI, of via de MCP-server. Getypte methoden zitten in de TypeScript-, Python-, PHP- en Go-SDK's onder email.templates. Volledige request- en responseschema's staan in de API-referentie. Verstuur een gepubliceerd template via het reguliere verzendendpoint.

Wat zit er in een template

Elk template heeft twee namen, en die doen verschillende dingen:

  • slug is de naam waarmee je het template verstuurt, bijvoorbeeld welcome-email. Je kiest die bij het aanmaken van het template en kunt hem daarna niet meer wijzigen. Een slug mag kleine letters, cijfers, koppeltekens en underscores bevatten, moet beginnen en eindigen met een letter of een cijfer, en mag maximaal 63 tekens lang zijn. Twee prefixen zijn niet toegestaan: bird_, dat is gereserveerd voor onze ingebouwde templates, en emt_, het formaat dat wordt gebruikt voor template-ID's. Het dashboard noemt dit veld Alias.
  • name is een vrij tekstveld als weergavelabel. Het staat standaard op de slug, en je kunt het op elk moment wijzigen. Niets wordt via de naam opgelost, dus het hernoemen van een template voor weergave breekt nooit een verzending.

Daarnaast heeft een template een permanent emt_-ID, dat vaststaat voor de hele levensduur. Het heeft ook een category, ofwel marketing ofwel transactional, en een source voor het opstellen: html, voltooide markup die je aanlevert, optioneel gepersonaliseerd met Liquid. De categorie en de bron liggen allebei vast bij het aanmaken van het template.

We bieden een catalogus van ingebouwde templates, en hun slugs beginnen allemaal met bird_. Een ingebouwd template hoort bij geen enkele werkruimte, kan niet worden bewerkt en is altijd direct verzendbaar. Kopieer er een naar je werkruimte om het van jou te maken, en het wordt een gewoon template dat je kunt bewerken. De kopie komt binnen als een niet-gepubliceerd concept dat de categorie, bron en taalinstellingen van het origineel overneemt, dus publiceer het voordat je het verstuurt.

Concepten en gepubliceerde versies

Elke template heeft precies één concept, de werkkopie die je bewerkt. Daarnaast heeft hij een willekeurig aantal gepubliceerde versies, elk genummerd (1, 2, 3, enzovoort) en nooit meer gewijzigd zodra hij bestaat. Bewerken verandert het concept ter plekke. Publiceren maakt een snapshot van het huidige concept, zet die om in de volgende genummerde versie en maakt die versie de versie die verzendingen gebruiken. Het concept zelf blijft bewerkbaar, zodat je aan de volgende kunt doorwerken.

De regel die ertoe doet bij verzenden is deze: een verzending gebruikt altijd de gepubliceerde versie van de template, en een concept wordt nooit op zichzelf verzonden. Je kunt het concept blijven bewerken terwijl een stabiele versie wordt verstuurd, en publiceren wanneer de wijziging klaar is. Een nieuwe versie publiceren verandert wat latere verzendingen renderen. Een verzending die al geaccepteerd is, wordt niet beïnvloed door een publicatie die daarna plaatsvindt.

Het tabblad Versies van de template met een Concept-rij en gepubliceerde v3-, v2- en v1-rijen met hun aanmaak- en publicatiedatums

Versies ondersteunen twee extra acties. Wijzigingen van het concept verwerpen om het concept terug te zetten naar wat er op dat moment gepubliceerd is. Of terugdraaien om een eerdere gepubliceerde versie weer de versie te maken die verzendingen gebruiken. Je kunt alleen terugdraaien naar een versie die gepubliceerd was, nooit naar het concept zelf. Terugdraaien vervangt het concept door de inhoud van die versie, dus alles wat niet opgeslagen is in het concept gaat verloren, en verder bewerken begint vanuit de versie waarnaar je hebt teruggedraaid. Een terugdraaiactie maakt geen nieuwe versie aan.

Om een bestaande afbeelding te kiezen heb je leestoegang tot de mediabibliotheek van de werkruimte nodig. Om een nieuwe afbeelding te uploaden, te plakken of neer te zetten heb je schrijftoegang nodig. Als Afbeelding invoegen is uitgeschakeld of je niet kunt bladeren of uploaden, vraag dan een werkruimtebeheerder om de bijbehorende mediabibliotheek-rechten. Bewerkingsrechten voor templates alleen geven geen toegang tot de mediabibliotheek.

Kies in het dashboard Visual > Insert image om in je mediabibliotheek te zoeken of een PNG-, JPEG-, GIF- of WebP-afbeelding tot 5 MB te uploaden. Statische WebP-uploads worden geconverteerd naar PNG of JPEG. Selecteer de afbeelding om de Afbeeldingsbeschrijving, weergavebreedte, uitlijning en link in te stellen. Markeer een afbeelding alleen als Decoratieve afbeelding wanneer deze geen informatie toevoegt; een gelinkte afbeelding heeft een beschrijving nodig die de bestemming uitlegt. Elke taal behoudt zijn eigen afbeeldingsbeschrijvingen en lay-out.

Gebruik Replace image om een geselecteerde afbeelding te vervangen met behoud van beschrijving, link, breedte en uitlijning. Je kunt ook één afbeeldingsbestand in de visuele editor plakken of neerzetten. Wacht tot de upload klaar is, of annuleer hem, voordat je opslaat of een test verstuurt. Controleer de preview en open dan More actions > Test email om de huidige inhoud naar jezelf te sturen. Code blijft beschikbaar om HTML te bewerken.

Een afbeelding uit de mediabibliotheek verwijderen verwijdert hem niet uit al verzonden e-mail. Een afbeelding vervangen gebruikt een nieuwe URL, dus eerdere berichten blijven het origineel tonen.

Opslaan wordt bewaakt door een revisienummer. Stuur de revision mee die je het laatst hebt gelezen voor de taal die je opslaat. Als iemand anders die taal in de tussentijd heeft gewijzigd, wordt het opslaan als conflict geweigerd in plaats van het werk van de ander te overschrijven. Laat revision weg om onvoorwaardelijk op te slaan. Publiceren en terugdraaien gebruiken de eigen revision van het concept op dezelfde manier.

Inhoud in meer dan één taal

Een template bevat inhoud in maximaal 25 talen, elk met een eigen onderwerp en body, getagd met een BCP-47-code zoals en of pt-BR. Eén taal is de standaard van de template. Publiceren van een template publiceert alle talen die hij bevat tegelijk. Je kunt niet één taal apart publiceren, dus ze moeten allemaal eerst af zijn. Elke taal heeft een onderwerp en een body nodig, en de standaardtaal van de template moet een van de talen zijn die je hebt ingevuld. Als iets daarvan ontbreekt, wordt er niets gepubliceerd, en de foutmelding vertelt je wat er per taal ontbreekt zodat je alles in één keer kunt oplossen. Je hoeft niet elke taal vooraf af te hebben: publiceer de talen die klaar zijn en voeg de rest later toe.

Een taal heeft een HTML-body nodig. Je kunt de text weglaten: publiceren maakt dan automatisch een platte-tekstalternatief van de HTML, zodat je beide delen krijgt zonder het tweede zelf te hoeven schrijven.

Elke taal kan ook previewtekst bevatten, ook wel preheader genoemd: de regel die een inbox na het onderwerp toont in de berichtenlijst. Die is optioneel, maximaal 255 tekens, en accepteert dezelfde {{ variable }}-placeholders als het onderwerp. Laat je hem weg, dan valt de inbox terug op de eerste regel van de body, en dat is zelden de regel die je zou kiezen. Publiceren weigert previewtekst op een taal waarvan de body geen HTML-deel heeft, omdat een mailclient de previewregel alleen uit verborgen HTML-opmaak leest, en weigert {{ bird.unsubscribe_url }} erin om dezelfde reden dat het onderwerp het niet kan bevatten: geen van beide is een plek waar een link kan staan.

Twee instellingen dekken een verzending die geen taal noemt waarvoor de template inhoud heeft, en ze beschermen tegen verschillende fouten:

InstellingWat het regelt
on_missing_languageWat er gebeurt wanneer een verzending een taal vraagt die de template niet heeft. fallback, de standaard, levert de beste overeenkomst. Het probeert eerst een bredere vorm van dezelfde taal, zodat een opgeslagen pt een verzoek voor pt-BR kan bedienen. Daarna valt het terug op de standaardtaal van de template. fail weigert de verzending in plaats daarvan, voor inhoud waarbij het verzenden in de verkeerde taal erger is dan helemaal niet verzenden.
language_source_requiredOf een verzending überhaupt een taal moet noemen. Dit staat standaard uit, zodat een verzending zonder taal de standaardtaal krijgt. Zet je het aan, dan wordt die verzending geweigerd. Een broadcast noemt één taal voor het hele publiek, dus een template met deze instelling aan vereist dat die taal gekozen is voordat de broadcast kan verzenden.

Je kunt deze twee onafhankelijk van elkaar instellen. Op zichzelf geldt fail alleen wanneer een verzending een taal noemt die we niet hebben, dus een verzending zonder taal komt er nog steeds doorheen. Zet beide instellingen samen aan als je wilt dat elke verzending bewust een taal noemt.

Personaliseren met variabelen

Schrijf {{ variable }}-placeholders in het onderwerp, de previewtekst en de body. We detecteren ze automatisch, gecombineerd over alle talen, dus je hoeft ze nooit apart te declareren. Het prefix van de placeholder onderscheidt de twee soorten. Een pad dat begint met bird. leest uit onze data, ofwel een contactrecord ofwel de uitschrijflink. Al het andere is een parameter waarvoor je bij het verzenden een waarde meegeeft.

De naam van een parameter is een enkel woord, zoals {{ animal }}. Een naam met punten verwijst naar een structuur die een parameter niet heeft, dus publiceren wordt geweigerd: schrijf de waarde als een eigen parameter, of lees contactdata met bird.contact.<attribute>.

Bij een enkele verzending of een batch komt de waarde van een parameter uit het template.parameters-object van de verzending, op basis van de naam. Eén set waarden geldt voor alle ontvangers van die verzending. bird is de enige naam die je daar niet kunt gebruiken: een template.parameters-sleutel met de naam bird wordt geweigerd met een 422.

Een broadcast heeft geen parameters-object, dus de inhoud kan alleen bird.-placeholders gebruiken. bird.contact.<attribute> wordt gevuld vanuit de eigen contacteigenschappen van elke ontvanger, en dat is wat de inhoud per ontvanger personaliseert. Elke contacteigenschap is beschikbaar via de eigen sleutel, en dat geldt ook voor de drie ingebouwde velden: first_name, last_name en email.

Codevoorbeeld
Hi {{ bird.contact.first_name }},

Elke parameter in het template heeft een waarde nodig als je verzendt. Anders retourneert de API een 422 die de ontbrekende parameter benoemt. Geef waarden op voor parameters in alle talen, omdat de geselecteerde taal kan afhangen van terugvalinstellingen. Een ontbrekende contacteigenschap wordt als een lege waarde weergegeven, dus voeg een terugvalwaarde toe voor klantgerichte inhoud: {{ bird.contact.first_name | default: "there" }}.

Een broadcast is strenger in welke namen het accepteert, omdat contacteigenschappen het enige zijn waarmee het placeholders kan vullen. De bird.contact.*-placeholders kunnen alleen een ingebouwd veld benoemen, of een contacteigenschap die de werkruimte heeft geregistreerd. Elke andere placeholder, inclusief een parameter, kan de broadcast niet invullen. De verzending wordt geweigerd en de foutmelding noemt de placeholder.

Een eigenschap archiveren verwijdert deze uit de eigenschappenkiezer in de editor. Nieuwe templateversies die de eigenschap uitlezen kunnen niet worden gepubliceerd, en de foutmelding noemt de eigenschap. Reeds gepubliceerde templates blijven verzenden. Dearchiveer de eigenschap om deze weer in nieuwe versies te gebruiken.

Placeholders gebruiken Liquid, dus filters en control flow werken naast gewone substitutie. Een {% if %}-conditional en een {% for %}-loop over een arraywaarde zijn beide prima. Een aantal constructies wordt geweigerd als je publiceert, en de foutmelding benoemt precies wat je moet aanpassen:

  • Partial includes, met {% include %} of {% render %}.
  • De increment-, decrement- en ifchanged-tags.
  • De money-, format_date-, format_time-, json-, inspect- en type-filters.
  • Vergelijken met empty of blank. Gebruik in plaats daarvan .size == 0.
  • Blokken die veel dieper genest zijn dan echte e-mailmarkup nodig heeft.

Het template van een broadcast kan helemaal geen {% for %}-loop gebruiken, omdat een broadcast per contacteigenschap één waarde invult en er niets is om over te itereren. Als je inhoud een loop nodig heeft, verzend deze dan via de berichten-API.

Elk template gebruikt Liquid, ook een dat alleen {{ variable }}-placeholders bevat. Voordat we publiceren, valideren we het onderwerp, de previewtekst, de HTML en de platte-tekstinhoud als Liquid. We voegen ook het escape-filter toe aan elke HTML-uitvoer die niet al eindigt op escape of escape_once, zodat een waarde met & of < de omringende opmaak niet kan wijzigen. De gereserveerde uitschrijf-uitvoer blijft ongewijzigd, zodat het verzenden deze kan vervangen. De onderwerpregel en de platte-tekstversie blijven zoals geschreven. Omdat publiceren deze filters toevoegt, is de HTML die je uit een gepubliceerde versie leest mogelijk niet byte-identiek aan wat je hebt ingediend.

Zet een volledige URL rechtstreeks in een href, zoals <a href="{{ sign_in_url }}">Sign in</a>. Voeg geen url_encode toe aan de hele waarde. Het percent-encodeert https://, /, ? en &, waardoor het resultaat niet meer werkt als absolute link. We voegen HTML-escaping toe terwijl de URL-structuur behouden blijft. Wanneer een parameter één URL-component levert, encodeer je dat component expliciet: <a href="https://example.com/search?q={{ query | url_encode }}">Search</a>.

Voorvertoning voordat je publiceert

Render een template met voorbeeldwaarden en ontvang de onderwerpregel en de HTML- en platte-tekstversie terug die een verzending zou opleveren. De voorvertoning gebruikt onze lokale Liquid-renderer en rendert standaard het concept, zodat je een wijziging kunt controleren voordat deze live gaat. Je kunt in plaats daarvan ook een gepubliceerde versie renderen. Het werkt voor je eigen templates en voor onze ingebouwde templates, en er wordt niets verzonden.

Je kunt ook zelf de inhoud aanleveren in plaats van het concept te laten lezen. Geef een onderwerp en body's mee en die worden gerenderd, precies zoals een concept behandeld zou worden. Zo kan een editor een wijziging tonen terwijl je typt, zonder dat er eerst iets opgeslagen hoeft te worden.

Personalisatie wordt voor je ingevuld, dus wat je terugkrijgt leest als afgewerkte tekst in plaats van {{ }}-placeholders. Geef een contact op en elke bird.contact.<attribute> wordt opgehaald uit de eigenschappen van dat contact. Zo controleer je je tekst tegen een echt record voordat iemand het ontvangt. De waarden komen uit dezelfde projectie die een broadcast gebruikt om zijn placeholders te vullen, dus de voorvertoning geeft hetzelfde antwoord als een verzending.

Laat contact weg en er worden plaatsvervangende waarden gebruikt: Bird en Test voor de voor- en achternaam, bird.test@example.com voor het e-mailadres en de eigen geregistreerde terugvalwaarde van elke andere eigenschap. Een eigenschap zonder terugvalwaarde wordt gerenderd als zijn sleutel tussen vierkante haken, zoals [loyalty_tier], wat je vertelt dat de waarde een placeholder is én welke eigenschap nog een terugvalwaarde nodig heeft.

Een contact wordt gelezen zoals het er nu uitziet. Dat maakt een voorvertoning het juiste middel om inhoud te controleren die je op het punt staat te verzenden, en het verkeerde om te vragen wat een eerdere verzending bevatte. Om te lezen wat een verzending daadwerkelijk heeft opgeleverd, open je dat bericht in het e-maillog, dat het rendert op basis van de waarden die die verzending meestuurde.

Voeg language toe om één specifieke taal te renderen, of laat het weg voor de standaardtaal van het template. Het antwoord vertelt je welke taal er is gerenderd, wat van belang is wanneer de gevraagde taal niet beschikbaar is en de on_missing_language van het template een nabije match heeft geserveerd.

Als het concept personalisatie bevat die bij publicatie zou worden geweigerd, geeft de voorvertoning dezelfde fout terug, dus het is ook een manier om problemen vroeg op te sporen.

In de template-builder van het dashboard toont Preview with contact data onderaan de linkerzijbalk de gerenderde e-mail naast wat je aan het bewerken bent, zowel in de visuele als in de code-editor. De kiezer eronder bepaalt wiens gegevens de placeholders vullen, en Sample data zijn de plaatsvervangende waarden hierboven.

Verzenden met een template

Stel het template-veld van de verzending in op een object dat het template benoemt, hetzij op id (emt_...), hetzij op slug, met precies één van de twee. Zet de variabelewaarden in template.parameters. Voeg language toe om een specifieke taal te kiezen, of laat het weg om de standaardtaal van het template te verzenden, tenzij het template vereist dat elke verzending er een opgeeft. Laat subject, html en text helemaal weg, want het template levert die al.

Codevoorbeeld
{
  "from": "hello@yourdomain.com",
  "to": ["delivered@messagebird.dev"],
  "category": "transactional",
  "template": {
    "slug": "welcome-email",
    "parameters": { "first_name": "Jane" }
  }
}

Eén gedrag om rekening mee te houden: de categorie van het template is een standaard, en de eigen category van de verzending overschrijft die. Laat category weg, en de verzending neemt de categorie van het template over, zodat een operationeel template als transactional verzendt zonder dat je het bij elke aanroep hoeft te herhalen. Stel category in, en jouw waarde wint. De rest van het verzendcontract staat in verzenden met een template.

Beheren buiten het dashboard

De volledige levenscyclus is beschikbaar buiten het dashboard: via de API, de SDK's onder email.templates, de bird CLI, en de email_templates_* MCP-tools. In de SDK's zitten de versie- en taaloperaties onder email.templates.versions en email.templates.versions.languages. De publicatiestap heet daar submit, en die maakt van het concept de volgende gepubliceerde versie.

Je kunt templates in je eigen repository bewaren en ze vanuit een buildstap synchroniseren. Een concept en een gepubliceerde versie geven andere HTML terug, dus kies de leesactie die bij je tool past:

  • Een concept geeft precies terug wat je hebt opgeslagen. De HTML die je in een concepttaal opslaat, komt byte voor byte terug uit de leesactie van die taal (GET /v1/email/templates/{id}/versions/{draft_version_id}/languages/{language}) en uit de versieleesactie, inclusief de doctype, <style>-blokken, commentaar, witruimte, regeleinden en niet-ASCII-tekst. Een opslagactie geeft geen inhoud terug, dus lees de taal terug als je wilt bevestigen wat er is opgeslagen.
  • Een gepubliceerde versie geeft verzendklare HTML terug. Publiceren herschrijft de HTML op drie manieren: elke {{ }}-uitvoer die nog niet eindigt op escape of escape_once krijgt het escape-filter, behalve {{ bird.unsubscribe_url }}, dat ongewijzigd blijft; previewtekst wordt als verborgen blok ingevoegd direct na de openende <body>-tag, of helemaal aan het begin als de HTML er geen heeft; en een leeg text wordt gevuld met een platte-tekstalternatief afgeleid van de HTML. Het lezen van die versie geeft de herschreven inhoud terug, dus die komt niet overeen met je bronbestand.

Om de bron te exporteren die je hebt geschreven, lees je het concept. Het lezen van een template of het opvragen van een templatelijst geeft metadata en status per taal terug, maar geen HTML.

De onderstaande voorbeelden gebruiken {{ first_name }}, een parameter: de verzending levert de waarde aan in template.parameters, zoals in "parameters": {"first_name": "Jane"}. Dat past bij een verzending naar een e-mailadres. Een {{ bird.contact.first_name }}-placeholder leest in plaats daarvan het contactrecord van de ontvanger, dat een broadcast vult vanuit de contacteigenschappen van elke ontvanger. Een verzending naar een e-mailadres heeft geen contactrecord, dus die weigert een template dat bird.contact.* leest met een 422 (EmailTemplateContactUnavailable).

Maak het template aan met de eerste taal. Het antwoord bevat de template-id en de draft_version_id, die de versie- en taaloperaties gebruiken. Het voorbeeld stelt ook preview_text in, de previewtekst die een inbox na het onderwerp in de berichtenlijst toont. Publiceren voegt die als verborgen blok in de HTML in, en dat is een van de redenen waarom een gepubliceerde versie andere HTML teruggeeft dan het concept:

const template = await bird.email.templates.create({
  slug: "welcome-email",
  category: "transactional",
  source: "html",
  languages: {
    en: {
      subject: "Welcome, {{ first_name }}",
      preview_text: "Your account is ready",
      html: "<!DOCTYPE html>\n<html>\n<body>\n<p>Hi {{ first_name }}, thanks for signing up.</p>\n</body>\n</html>\n",
    },
  },
});
console.log(template.id, template.draft_version_id);

Om een taal later te wijzigen, sla je die opnieuw op met bird email templates versions languages set of een PUT naar hetzelfde taalpad. De body is plat: subject, dat verplicht is, plus preview_text, html en text. Een veld dat je weglaat wordt gewist. Voeg de revision toe die je het laatst hebt gelezen om de opslagactie te weigeren als iemand anders die taal inmiddels heeft gewijzigd.

De visuele-editorindeling van het dashboard is een van de velden die op deze manier worden gewist, omdat de API, SDK's en CLI die niet kunnen meesturen. Het opslaan van een taal die je in de visuele editor hebt opgebouwd behoudt de HTML maar verwijdert de indeling, waardoor het dashboard die taal weer opent in de code-editor.

De volgende stappen gebruiken TEMPLATE_ID en DRAFT_VERSION_ID voor de id en draft_version_id die de create-aanroep heeft teruggegeven. Lees de concepttaal terug om de HTML precies te krijgen zoals die is opgeslagen:

const language = await bird.email.templates.versions.languages.get(
  "TEMPLATE_ID",
  "DRAFT_VERSION_ID",
  "en",
);
console.log(language.content?.html);

jq -j schrijft de string zonder een afsluitende newline toe te voegen, zodat het bestand dezelfde bytes bevat als de API heeft opgeslagen.

Dien het concept eerst in met validate_only. Dat voert dezelfde controles uit als een echte indiening over alle talen en rapporteert de gevonden problemen in één antwoord, zonder iets te publiceren. Dien daarna in zonder validate_only om de volgende versie te publiceren, die verzendingen dan gebruiken:

const templateId = "TEMPLATE_ID";
const draftVersionId = "DRAFT_VERSION_ID";
const check = await bird.email.templates.versions.submit(templateId, draftVersionId, {
  validate_only: true,
});
for (const problem of check.errors) {
  console.log(problem.language, problem.field, problem.message);
}
if (check.valid) {
  const result = await bird.email.templates.versions.submit(templateId, draftVersionId);
  console.log(result.version?.version_number);
}

De indieningsbody neemt validate_only en expected_revision. Geef de revision van het template door als expected_revision om de publicatie te weigeren als het concept is gewijzigd sinds je het voor het laatst hebt gelezen.

Toon de templates van je werkruimte om de slug te vinden die een verzending noemt:

for await (const tpl of bird.email.templates.list({ scope: "workspace" })) {
  console.log(tpl.slug, tpl.name);
}

Verzend het gepubliceerde template op slug, met een waarde in template.parameters voor elke parameter die het gebruikt, hier first_name:

const msg = await bird.email.send({
  from: { email: "onboarding@messagebird.dev", name: "Bird" },
  to: ["delivered@messagebird.dev"],
  category: "transactional",
  template: {
    slug: "welcome-email",
    parameters: { first_name: "Jane" },
  },
});
console.log(msg.id, msg.status);

Vervolgstappen

  • E-mail verzenden: de volledige verzendpayload en hoe templateverzendingen daarin passen
  • Categorieën: marketing vs transactional kiezen per verzending
  • bird email templates: templates beheren vanuit de terminal
  • API-referentie: volledige request- en responseschema's voor alle achttien templatebewerkingen
  • SDK's: de getypeerde email.templates-methoden in TypeScript, Python, PHP en Go
  • MCP server: een agent templates laten schrijven en publiceren
  • Een e-mailtemplate bouwen: een video waarin er een in het dashboard wordt gebouwd en vervolgens een agent er nog een maakt

Ga verder met de documentatie, handleidingen en voorbeelden voor dit onderwerp.