Developers build on all kinds of stacks, so here is the same integration written per stack — copy the code for yours. Every example talks to one endpoint, POST /api/v1/plugin/findings, with your site API key in the X-Api-Key header (full contract in the API reference below). Each example starts with a ping — the quickest first request: it proves the connection and marks the site connected without recording a scan. Pings and reports need a plan that includes the WordPress connector (Starter and above); on the Free plan the endpoint answers 403 wordpress_connector_not_in_plan.
Any stack — HTTP contract (cURL)
Every integration speaks this exact contract: POST JSON, key in the header, read the JSON back. If your stack is not listed below (Go, Ruby, Java, Rust, Postman...), copy this request — it works everywhere unchanged.
Code to add
curl -X POST https://your-dashboard.example.com/api/v1/plugin/findings \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: YOUR_SITE_API_KEY' \
-d '{"events":[{"type":"ping"}]}'
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- Replace https://your-dashboard.example.com with your dashboard URL and YOUR_SITE_API_KEY with the key from Dashboard → Sites → your site → API key.
- Expected answer: {"message":"Connected.","connected":true} — the site then shows status connected. Pings never create a scan.
- Swap the ping for a findings or events array to report real data — full payload schema is in the API reference below.
- Keep the API key on your server — never in browser JavaScript or a public repository.
Plain PHP (custom code, CMS plugins)
Works on any PHP host: shared hosting, custom CMS, a plain script or a cron job. curl ships with PHP 8, so no Composer package is needed.
Code to add
$payload = json_encode([
'events' => [['type' => 'ping']],
]);
$ch = curl_init('https://your-dashboard.example.com/api/v1/plugin/findings');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'X-Api-Key: '.getenv('SUMS_API_KEY'),
],
CURLOPT_POSTFIELDS => $payload,
]);
$response = curl_exec($ch);
curl_close($ch);
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- Put SUMS_API_KEY=your_key in the server environment (or a .env file) and read it with getenv().
- Run it once to pair (site turns connected), then call it from your scheduled job with the payload you want to report.
- For real reports, replace the ping body with a findings array: code, title, severity (critical, high, medium, low, info) and optionally description, threat and evidence.
Laravel (HTTP client)
Laravel applications send the report with the framework HTTP client — it fakes cleanly in tests, so your test suite never hits the real API.
Code to add
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'X-Api-Key' => config('sums.api_key'),
])->timeout(10)->post(
'https://your-dashboard.example.com/api/v1/plugin/findings',
['events' => [['type' => 'ping']]],
);
$connected = $response->successful();
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- Add SUMS_API_KEY=your_key to .env and read it through config() or env().
- In tests use Http::fake() instead of the real request.
- Keep the key in config, not in the repository — it identifies your site to the dashboard.
Node.js (Express, Vercel, Cloudflare Workers)
Node 18+ ships fetch, so no dependency is required. The same code runs inside an Express route, a Vercel or Netlify serverless function and a Cloudflare Worker.
Code to add
const response = await fetch(
'https://your-dashboard.example.com/api/v1/plugin/findings',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': process.env.SUMS_API_KEY,
},
body: JSON.stringify({ events: [{ type: 'ping' }] }),
},
);
const data = await response.json(); // { message: "Connected.", connected: true }
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- Set SUMS_API_KEY in the server environment: .env locally, the host dashboard in production.
- Call it from a cron, a webhook route or an after-login hook — wherever your events happen.
- Never call this from client-side code: the key would be visible in the browser.
Python (Django, Flask, scripts)
One requests call — from a Django management command, a Flask endpoint, an Airflow task or a plain cron script.
Code to add
import os
import requests
response = requests.post(
'https://your-dashboard.example.com/api/v1/plugin/findings',
headers={'X-Api-Key': os.environ['SUMS_API_KEY']},
json={'events': [{'type': 'ping'}]},
timeout=10,
)
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- pip install requests
- Export SUMS_API_KEY in the environment, then run the script from cron or your task queue.
- The json= argument sets the body and Content-Type for you — pass dictionaries the same way when reporting findings.
Go (net/http)
Standard library only — net/http posts the JSON with the key from the environment, no modules to vendor.
Code to add
package main
import (
"bytes"
"fmt"
"io"
"log"
"net/http"
"os"
)
func main() {
body := []byte(`{"events":[{"type":"ping"}]}`)
req, err := http.NewRequest(http.MethodPost, "https://your-dashboard.example.com/api/v1/plugin/findings", bytes.NewReader(body))
if err != nil {
log.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Api-Key", os.Getenv("SUMS_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- export SUMS_API_KEY=your_key, then go run report.go.
- For services, use an http.Client with a timeout instead of http.DefaultClient.
- Swap the ping body for your findings payload when reporting real data.
Ruby (Rails / plain)
net/http from plain Ruby or a Rails background job — no gem required; in Rails put the key in credentials or ENV.
Code to add
require "net/http"
require "json"
uri = URI("https://your-dashboard.example.com/api/v1/plugin/findings")
request = Net::HTTP::Post.new(uri)
request["Content-Type"] = "application/json"
request["X-Api-Key"] = ENV.fetch("SUMS_API_KEY")
request.body = { events: [{ type: "ping" }] }.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
end
puts response.body
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- export SUMS_API_KEY=your_key, then ruby report.rb.
- In Rails, run it from an ActiveJob or Rake task so the key stays server-side.
- ENV.fetch raises when the key is missing — it fails fast instead of reporting to the wrong site.
Java (Spring Boot)
Java 11+ HttpClient from a service or scheduled job — no extra dependency; in Spring the same call fits a RestTemplate bean or a @Scheduled method.
Code to add
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://your-dashboard.example.com/api/v1/plugin/findings"))
.header("Content-Type", "application/json")
.header("X-Api-Key", System.getenv("SUMS_API_KEY"))
.POST(HttpRequest.BodyPublishers.ofString("{\"events\":[{\"type\":\"ping\"}]}"))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- Set SUMS_API_KEY in the service environment (systemd, Kubernetes, or your IDE run config).
- Snippet uses the Java 11+ HttpClient — in Spring, restTemplate.exchange(...) posts the same body.
- Wrap send(...) in try/catch (IOException, InterruptedException) in real code.
C# (.NET / HttpClient)
PostAsJsonAsync from .NET 6+ (top-level statements) — the JSON helper is built in, so only the framework is needed.
Code to add
using System.Net.Http.Json;
var client = new HttpClient();
client.DefaultRequestHeaders.Add(
"X-Api-Key",
Environment.GetEnvironmentVariable("SUMS_API_KEY") ?? ""
);
var response = await client.PostAsJsonAsync(
"https://your-dashboard.example.com/api/v1/plugin/findings",
new { events = new[] { new { type = "ping" } } }
);
Console.WriteLine(await response.Content.ReadAsStringAsync());
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- Set SUMS_API_KEY in the process environment before dotnet run.
- In ASP.NET Core register a typed client with services.AddHttpClient(...) instead of new.
- PostAsJsonAsync serialises the object for you — use the same shape when reporting findings.
Flutter (Dart)
Dart on a server, Cloud Function or test runner: the http package posts the JSON. Inside a phone app, do NOT embed the key — send events to your own backend and the server calls this API.
Code to add
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
final response = await http.post(
Uri.parse('https://your-dashboard.example.com/api/v1/plugin/findings'),
headers: {
'Content-Type': 'application/json',
'X-Api-Key': Platform.environment['SUMS_API_KEY'] ?? '',
},
body: jsonEncode({'events': [{'type': 'ping'}]}),
);
print(response.body);
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- Add the client with flutter pub add http.
- For phone apps, post to YOUR server first; only the server holds SUMS_API_KEY.
- jsonEncode builds the body — pass the findings map the same way when reporting.
Swift (iOS / server)
URLSession from a Swift script, macOS tool or server-side Swift. In an iOS app, keep the key on your server instead — anything shipped in the binary can be extracted.
Code to add
import Foundation
var request = URLRequest(
url: URL(string: "https://your-dashboard.example.com/api/v1/plugin/findings")!
)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(
ProcessInfo.processInfo.environment["SUMS_API_KEY"] ?? "",
forHTTPHeaderField: "X-Api-Key"
)
request.httpBody = try? JSONSerialization.data(
withJSONObject: ["events": [["type": "ping"]]]
)
URLSession.shared.dataTask(with: request) { data, _, _ in
print(String(data: data ?? Data(), encoding: .utf8) ?? "")
}.resume()
Example response
{
"message": "Connected.",
"connected": true
}
Setup notes
- Export SUMS_API_KEY, then swift report.swift (Swift 5+ toolchain).
- In an iOS app, relay events through your own server so the key never reaches the device.
- JSONSerialization builds the body — swap the ping dictionary for findings.
WordPress — no code at all
WordPress sites skip the code entirely: the official Sums Security Guard plugin is a ready-made client of this same API — install it, paste the key, done. Step-by-step install and setup are in the WordPress section below.
Setup notes
- Download the plugin zip from your profile (Where to get the plugin, below).
- Upload it in wp-admin under Plugins → Add New → Upload Plugin, then Activate.
- Paste the dashboard URL and API key under Settings → Sums Guard and click Test connection.