Elke sprint begint met een bord vol tickets en een team dat ergens schoon moet kunnen werken. Voor bureaus die WordPress projecten voor klanten uitvoeren met cycli van twee weken, betekent dit dat ze een testomgeving in MyKinsta moeten creëren voordat het eerste ticket wordt opgehaald.
Dit kost een paar minuten, maar het is het soort taak dat door de mazen van het net valt omdat het zo weinig lijkt.
De Kinsta API kan die stap voor je weghalen. Wanneer een sprint start in Jira, kun je een webhook instellen die een event in middleware triggert, die vervolgens de payload leest, deze koppelt aan een Kinsta site en de API aanroept om een nieuwe testomgeving te maken.
Waarom bureaus de provisioning van omgevingen zouden moeten automatiseren
Een omgeving aanmaken na het plannen van een sprint betekent MyKinsta openen, de juiste klantsite vinden uit een lijst van tientallen, een omgeving aanmaken en een naam geven en dan terugkeren naar Jira. Hoewel dit niet ingewikkeld is, moet het wel op het juiste moment gebeuren, elke keer weer, en voor elk klantproject dat loopt.
Als je dit overslaat, begint een team te werken in de omgeving van de laatste sprint. Van daaruit stapelen de wijzigingen zich op en als er een bug is, lijkt het isoleren ervan meer op archeologie dan op debuggen.
Wat je nodig hebt voordat je begint
Om de Kinsta API en Jira te verbinden, heb je een Kinsta-account nodig met ten minste één WordPress site in een bestaande omgeving, een Jira Cloud account met beheerderstoegang om webhooks te configureren en Node.js lokaal geïnstalleerd.
Om je te authenticeren met de Kinsta API, navigeer je naar [Jouw bedrijf] > Bedrijfsinstellingen > API-sleutels in MyKinsta en klik je op API-sleutel aanmaken.

Geef de sleutel vervolgens een naam, stel een vervaldatum in en klik op Genereer. De sleutel wordt eenmalig weergegeven, dus noteer hem voordat je verder gaat.
Je zet deze in een .env bestand in de project root naast je Kinsta bedrijfs-ID, die je kunt vinden onder Bedrijfsinstellingen > Factureringsgegevens:
KINSTA_API_KEY=your_api_key_here
KINSTA_COMPANY_ID=your_company_id_here
Je Jira en Kinsta site ID’s ophalen
Je hebt de Kinsta site ID nodig voor elk klantproject in de automatisering. Dit is een UUID die Kinsta toekent bij het aanmaken van de site. Het verschijnt in de MyKinsta URL wanneer je een site opent of door GET /sites aan te roepen zodra je API-sleutel is ingesteld:
https://my.kinsta.com/sites/details/fbab4927-e354-4044-b226-29ac0fbd20ca/…
Aan de Jira-kant heb je de numerieke board-ID nodig voor elk project dat je wilt verbinden. Deze verschijnt in de URL (hier als 2):
https://your-domain.atlassian.net/jira/software/projects/SCRUM/boards/2
Dit is dezelfde waarde die Jira opneemt in de sprint_started webhook payload als originBoardId. De mapping van board ID’s naar site ID’s staat in je .env bestand:
BOARD_ID_CLIENT_A=2
SITE_ID_CLIENT_A=fbab4927-e354-4044-b226-29ac0fbd20ca
BOARD_ID_CLIENT_B=5
SITE_ID_CLIENT_B=44b5a6d1-c83f-4b0e-9a1c-2e7dbc903fa1
Voor lokale ontwikkeling kan Jira ook niet direct localhost bereiken. Ngrok kan worden gebruikt om een lokale poort bloot te stellen aan het internet met een tijdelijke openbare URL, die je kunt gebruiken als je webhook endpoint tijdens de ontwikkeling. Zodra je een ingezet middleware adres hebt, kun je het vervangen.
Zo automatiseer je de sprintomgeving provisioning met Jira en de Kinsta API
Deze integratie gaat over twee systemen. In Jira vuurt een webhook wanneer een sprint start en levert de payload van de event aan je middleware. Voor Kinsta leest de middleware de board-ID uit de payload, zet deze om in een site-ID met behulp van de config map en roept de Kinsta API aan om een gewone testomgeving aan te maken met de naam van de sprint.
1. Registreer een Jira webhook voor sprintevents
Jira Cloud geeft je twee manieren om een webhook te registreren. De eenvoudigste optie voor de meeste teams is via de Jira UI. De optie Settings > System staat in het menu rechtsboven:

Kies van daaruit Advanced > WebHooks en klik vervolgens op Create a webhook:

Voer hier een naam in, plak een middleware URL met /sprint toegevoegd (een dummy optie is prima voor nu) en selecteer onder Events, dan Sprint > started. Dit maakt een admin webhook aan, die vuurt voor elke sprint_started event in je hele Jira instance.

De tweede optie is de REST API, met POST /rest/webhooks/1.0/webhook. Dit werkt goed als de registratie van webhooks deel uitmaakt van een implementatiescript:
curl -X POST \
https://your-domain.atlassian.net/rest/webhooks/1.0/webhook \
-u [email protected]:your-api-token \
-H 'Content-Type: application/json' \
-d '{
"name": "Sprint provisioning webhook",
"url": "https://your-middleware-url.com/sprint",
"events": ["sprint_started"],
"filters": {},
"excludeBody": false
}'
Het aanroepen van PUT /rest/webhooks/1.0/webhook/refresh voegt een verlenging toe aan de 30-daagse verlooptijd voor de webhook. Wanneer Jira sprint_started afvuurt, komt de payload aan op je endpoint als een JSON POST met de volgende structuur:
{
"timestamp": 1705431600000,
"webhookEvent": "sprint_started",
"sprint": {
"id": 15,
"self": "https://your-domain.atlassian.net/rest/agile/1.0/sprint/15",
"state": "active",
"name": "Sprint 12",
"startDate": "2026-02-02T00:00:00.000Z",
"endDate": "2026-02-27T00:00:00.000Z",
"originBoardId": 2,
"goal": "Complete payment processing improvements"
}
}
De middleware gebruikt sprint.originBoardId om de Kinsta site ID op te zoeken en sprint.name om de nieuwe omgeving een naam te geven: elke sprint_started event binnen je Jira instance bereikt het endpoint. De board ID lookup in de config map scoopt de automatisering naar het juiste klantproject en negeert al het andere.
2. Bouw het middleware endpoint
Met de webhook op zijn plaats moet je nu een nieuw Node.js project initialiseren en Express.js installeren naast dotenv:
npm init -y
npm install express dotenv
express zorgt voor de routing en request parsing, terwijl dotenv je .env bestand laadt. Je moet app.js maken om de server in te stellen. Hier is het complete bestand:
// app.js
const express = require('express');
const crypto = require('crypto');
require('dotenv').config();
const app = express();
// Raw body parser on the /sprint route enables HMAC signature verification
app.use('/sprint', express.raw({ type: 'application/json' }));
app.use(express.json());
const KinstaAPIUrl = 'https://api.kinsta.com/v2';
const headers = {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.KINSTA_API_KEY}`
};
// Board ID to Kinsta site ID config map
const siteConfig = {
[process.env.BOARD_ID_CLIENT_A]: process.env.SITE_ID_CLIENT_A,
[process.env.BOARD_ID_CLIENT_B]: process.env.SITE_ID_CLIENT_B,
};
function verifyJiraSignature(req) {
const signature = req.headers['x-hub-signature'];
const secret = process.env.JIRA_WEBHOOK_SECRET;
if (!signature || !secret) return false;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
app.post('/sprint', async (req, res) => {
if (!verifyJiraSignature(req)) {
return res.status(401).json({ message: 'Invalid signature' });
}
const body = JSON.parse(req.body);
const { webhookEvent, sprint } = body;
if (webhookEvent !== 'sprint_started') {
return res.status(200).json({ message: 'Event ignored' });
}
const boardId = String(sprint.originBoardId);
const siteId = siteConfig[boardId];
if (!siteId) {
console.log(`No site configured for board ${boardId}`);
return res.status(200).json({ message: 'Board not mapped' });
}
// Kinsta API calls added in the steps below
res.status(200).json({ message: 'Received' });
});
app.listen(3000, () => console.log('Middleware running on port 3000'));
Om het endpoint te beschermen, genereer je een geheime sleutel tijdens het instellen van de webhook. Jira maakt dit optioneel tijdens het instellen, maar het is praktisch essentieel voor een veilige instance.
Beveiliging van het endpoint
Jira ondertekent elke payload en neemt het resultaat op in de X-Hub-Signature header als sha256=<hash>. Je voegt het geheim toe aan je .env bestand naast de andere referenties:
JIRA_WEBHOOK_SECRET=your_webhook_secret_here
De verificatiefunctie leeft in app.js en gebruikt de ingebouwde cryptomodule van Node. Het leest de signature uit de request header, berekent de verwachte HMAC tegen de raw request body en gebruikt timingSafeEqual om ze te vergelijken op een manier die timing aanvallen voorkomt. Hier is het relevante gedeelte van app.js:
const crypto = require('crypto');
function verifyJiraSignature(req) {
const signature = req.headers['x-hub-signature'];
const secret = process.env.JIRA_WEBHOOK_SECRET;
if (!signature || !secret) return false;
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(req.body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
Deze functie wordt als eerste aangeroepen in de POST /sprint routeafhandeling. Als de verificatie mislukt, retourneert de middleware onmiddellijk 401 en wordt er verder niets uitgevoerd:
app.post('/sprint', async (req, res) => {
if (!verifyJiraSignature(req)) {
return res.status(401).json({ message: 'Invalid signature' });
}
const body = JSON.parse(req.body);
// …rest of the handler
});
De route gebruikt express.raw() op het /sprint pad omdat verifyJiraSignature dit nodig heeft om de HMAC te berekenen. Zodra de verificatie geslaagd is, geeft JSON.parse(req.body) hetzelfde resultaat als express.json() zou hebben.
3. Authenticeer met de Kinsta API en haal site-omgevingen op
Alle verzoeken aan de Kinsta API maken gebruik van Bearer token authenticatie: de headers constante in app.js handelt dat af voor elk verzoek in de applicatie. De regel require('dotenv').config() bovenaan zorgt ervoor dat de sleutel wordt geladen vanuit .env voordat er iets anders wordt uitgevoerd, dus het verschijnt nooit in de broncode zelf.
Kinsta gebruikt omgeving ID’s in plaats van site ID’s voor het provisioning endpoint, dus je moet een getEnvironmentId functie toevoegen onder de headers constante:
const getEnvironmentId = async (siteId) => {
const resp = await fetch(
`${KinstaAPIUrl}/sites/${siteId}/environments`,
{ method: 'GET', headers }
);
const data = await resp.json();
return data.site.environments[0].id;
};
Dit roept GET /sites/{siteId}/omgevingen aan en retourneert de ID van de eerste (d.w.z. live) omgeving in de respons. Als een site meerdere omgevingen gebruikt en je moet je richten op een specifieke, match dan met de omgevingsnaam in plaats van het eerste resultaat te nemen.
4. Maak een standaard testomgeving met de Kinsta API
Met de site- en omgevings-ID’s in kannen en kruiken, roept de middleware POST /sites/{siteId}/environments/plain aan om de sprintomgeving te maken. Je doet dit via een functie createSprintEnvironment onder getEnvironmentId:
const createSprintEnvironment = async (siteId, sprintName) => {
const resp = await fetch(
`${KinstaAPIUrl}/sites/${siteId}/environments/plain`,
{
method: 'POST',
headers,
body: JSON.stringify({
display_name: sprintName,
is_premium: false
})
}
);
const data = await resp.json();
return data;
};
display_name verschijnt in MyKinsta, terwijl het rechtstreeks gebruik van sprint.name uit de Jira payload betekent dat elke omgeving in het dashboard overeenkomt met de sprint waar deze bij hoort. De flag is_premium bepaalt of Kinsta dit als een standaard of premium testomgeving beschouwt. Als deze op false wordt gezet, wordt een standaardomgeving gecreëerd.
Wanneer het verzoek Kinsta bereikt, retourneert het 202 Accepted met een operation_id in plaats van een voltooide omgeving:
{
"operation_id": "environments:add-plain-54fb80af-576c-4fdc-ba4f-b596c83f15a1",
"message": "Adding plain environment in progress",
"status": 202
}
Kinsta’s async verwerking voorkomt een geblokkeerde request thread terwijl de provisioning wordt voltooid. De operation_id is wat je doorgeeft aan het endpoint om de voortgang bij te houden. Werk vervolgens de POST /sprint route bij om beide functies achter elkaar aan te roepen:
app.post('/sprint', async (req, res) => {
if (!verifyJiraSignature(req)) {
return res.status(401).json({ message: 'Invalid signature' });
}
const body = JSON.parse(req.body);
const { webhookEvent, sprint } = body;
if (webhookEvent !== 'sprint_started') {
return res.status(200).json({ message: 'Event ignored' });
}
const boardId = String(sprint.originBoardId);
const siteId = siteConfig[boardId];
if (!siteId) {
console.log(`No site configured for board ${boardId}`);
return res.status(200).json({ message: 'Board not mapped' });
}
try {
const envId = await getEnvironmentId(siteId);
const result = await createSprintEnvironment(siteId, sprint.name);
res.status(200).json(result);
} catch (err) {
console.error(err);
res.status(500).json({ message: 'Environment creation failed' });
}
});
Het gebruik van het try block is schoner dan vertrouwen op meerdere if statements. Houd de verificatie van de Jira signature echter bovenaan het bestand, omdat deze vóór alle andere code moet worden uitgevoerd.
5. Controleer de status van de operatie en bevestig de provisioning
Om de voltooiing te volgen, peil je GET /operations/{operation_id} totdat de status 200 is met behulp van een pollOperation-functie onder createSprintEnvironment:
const pollOperation = async (operationId, intervalMs = 5000, maxAttempts = 12) => {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
await new Promise(resolve => setTimeout(resolve, intervalMs));
const resp = await fetch(
`${KinstaAPIUrl}/operations/${operationId}`,
{ method: 'GET', headers }
);
const data = await resp.json();
if (data.status === 200) {
console.log(`Environment ready: ${operationId}`);
return data;
}
if (data.status >= 400) {
throw new Error(`Operation failed: ${data.message}`);
}
}
throw new Error('Operation timed out after maximum attempts');
};
De loop wacht vijf seconden tussen elke poging en bevat maximaal een minuut aan provisioningtijd. Terwijl 200 de voltooiing aangeeft, geeft elke 4xx status aan dat het onderzoek is mislukt.

Als je dit allemaal uitvoert met node app.js en een sprint start in Jira, zou de omgeving binnen een minuut of twee in MyKinsta moeten verschijnen.
Sprint met je bureau voorbij de concurrentie
Deze integratie zorgt voor een schone, gelabelde testomgeving binnen MyKinsta op basis van het starten van een sprint in Jira. De webhook gaat af, de middleware zet de board-ID om naar een site, de Kinsta API regelt de rest en het team haalt hun tickets op met een omgeving die al op hen wacht.
Als de middleware klaar is om live te gaan, is Sevalla een eenvoudig implementatiedoel. Je pusht het project naar een Git provider, verbindt de repo, voegt de omgevingsvariabelen toe en werkt de Jira webhook URL bij naar het live adres.
Bovendien is het Agency Partner Programma van Kinsta ideaal voor bureaus die meerdere klantprojecten beheren. Het geeft je toegewijde ondersteuning, co-marketingmogelijkheden en het soort infrastructuurpartnerschap dat de automatiseringslaag ondersteunt die je bouwt bovenop de Kinsta API.