Integration docs

Documentation

Everything you need to connect a site or service to this dashboard: pick your stack and copy the code, read the full API contract, or install the no-code WordPress plugin. Every integration speaks the same language — JSON over HTTP with your site API key.

Quick start

  1. 1. Create your account

    Register free and verify your email — the dashboard unlocks as soon as the address is verified.

  2. 2. Register your site

    Dashboard → Sites → Add site. Every site gets its own API key, which identifies it to the API.

  3. 3. Choose how to connect

    Any platform — WordPress, PHP, Node, Python or static HTML — can use the REST API with the site API key. WordPress sites can skip the code entirely with the plugin — syncing it needs the Starter plan or above.

  4. 4. Make the first connection

    Send a ping request with curl (example below), or upload the plugin zip in wp-admin and paste your dashboard URL and API key under Settings → Sums Guard. Without a plan that includes the connector the API answers 403 wordpress_connector_not_in_plan and the plugin keeps protecting locally.

  5. 5. See the first report

    Click Test connection or send the ping — on Starter and above, security score, findings and email alerts start showing up right away.

Add the API — pick your stack

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.

REST API reference

One small versioned JSON API powers every integration — from any language or platform: PHP, Node, Python, mobile apps or plain curl. The WordPress plugin further down is only a ready-made client of these same endpoints; if your site does not run WordPress, call the API directly with the examples below.

Base URL

https://your-dashboard.com/api/v1

Authentication & rate limits

  • Every request must carry the site API key in the X-Api-Key header (Authorization: Bearer <key> also works).
  • Copy the key from Dashboard → Sites → your site → API key — or regenerate it on the same card.
  • A missing or unknown key returns 401 with {"message": "Invalid or missing API key.", "code": "invalid_api_key"}.
  • A key this dashboard has replaced stays recognisable for 30 days: it returns 401 with {"code": "key_replaced"} and the date it was replaced, so a stale config is told exactly why it stopped working and where to copy the current key from.
  • Every accepted request stamps the site’s "Last seen" heartbeat; a site with no contact for 48 hours is marked Disconnected automatically — even when it carries no plugin.
  • Rate limits per key: findings 60/min, update check 30/min, package download 10/min.
POST /plugin/findings

Report findings & events

The connector sends file-integrity and malware findings plus login events here. Each report is stored as its own scan, recalculates the security score and triggers email alerts. Rate limit: 60 requests/minute.

Example request

curl -X POST https://your-dashboard.example.com/api/v1/plugin/findings \
  -H 'X-Api-Key: YOUR_SITE_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "findings": [
      {
        "code": "malware-uploaded-shell",
        "title": "PHP shell found in uploads",
        "severity": "critical",
        "description": "wp-content/uploads/2026/shell.php executes arbitrary commands.",
        "threat": "unrestricted-file-upload",
        "evidence": { "path": "wp-content/uploads/2026/shell.php", "sha1": "abc123" }
      }
    ],
    "events": [
      { "type": "login_failed", "username": "admin", "ip": "203.0.113.10" }
    ]
  }'

Example response

HTTP/1.1 201 Created
{
  "message": "Findings stored.",
  "scan_id": 42,
  "score": 70,
  "findings": 1,
  "events": 1
}
GET /plugin/update

Latest plugin version

Version manifest for the plugin: the latest version, where to download it, and the WordPress and PHP minimums. Sums Guard is not published on WordPress.org — updates ship from this dashboard, so this manifest is what the plugin checks when looking for a new version. Rate limit: 30 requests/minute.

Example request

curl -H 'X-Api-Key: YOUR_SITE_API_KEY' \
  https://your-dashboard.com/api/v1/plugin/update

Example response

HTTP/1.1 200 OK
{
  "slug": "sums-security-guard",
  "version": "1.0.0",
  "package": "https://your-dashboard.com/api/v1/plugin/package",
  "url": "https://your-dashboard.com/download-plugin",
  "requires": "6.0",
  "requires_php": "8.0"
}
GET /plugin/package

Download plugin zip (latest)

Streams the installable zip of the newest plugin version, built on the fly from the dashboard source. Installs and updates come from this dashboard — this endpoint serves the same zip the plugin’s updater downloads. Rate limit: 10 requests/minute.

Example request

curl -H 'X-Api-Key: YOUR_SITE_API_KEY' \
  -o sums-security-guard.zip \
  https://your-dashboard.com/api/v1/plugin/package

Example response

HTTP/1.1 200 OK
Content-Type: application/zip
Content-Disposition: attachment; filename=sums-security-guard-1.0.0.zip

<binary zip body>

Payload reference

  • findings[] — up to 500 items per report. Required per item: code (max 80 chars), title, severity (critical | high | medium | low | info). Optional: description, threat (knowledge-base slug that links the finding to an article), evidence (key/value map such as path and sha1).
  • events[] — up to 200 items per report. type is one of: login_failed, login_success_suspicious, user_created, password_reset, ping. Optional: username, ip, user_agent, message, happened_at.
  • A report containing only a ping event is a connection test: it marks the site connected without creating a scan or sending alerts.
  • A report with neither findings nor events is rejected with 422. An invalid severity also returns 422 with field-level errors.
  • If the dashboard is unreachable, the plugin queues reports locally (up to 500) and resends them on the next run.

How scoring works

  • Every scan starts at 100 points; each finding deducts by severity: critical -25, high -12, medium -5, low -2, info -0.
  • The score is returned in the response (score) and stored on the site latest scan.
  • Authentication errors return 401 — invalid_api_key for an unknown key, key_replaced for one this dashboard retired. Plan gating returns 403 with license_expired (the subscription lapsed) or wordpress_connector_not_in_plan (the plan does not include the WordPress connector); validation errors are 422 and rate-limit overloads 429, all as JSON.

WordPress — the easy way

If your site runs WordPress you write no code at all: install the ready-made Sums Security Guard plugin, paste your API key, and it handles the rest — file integrity monitoring, daily malware scans, login monitoring and real-time brute-force blocking, all reported to this dashboard through the API above.

Where to get the plugin

  1. Log in to your Sums Solution account (your email must be verified). The download section sits on your profile and is available on every plan — syncing its findings to the dashboard starts on Starter.
  2. Open Profile → WordPress plugin → "Download plugin (.zip)" — the direct link is /download-plugin.
  3. The plugin is not published on WordPress.org — this dashboard is its only official source, and the zip always contains the latest released code.
Log in to download Free account required — the download lives in your profile.

Install — three clicks

  1. In wp-admin go to Plugins → Add New → Upload Plugin.
  2. Select sums-security-guard.zip, click Install Now, then Activate.
  3. Open the new Settings → Sums Guard menu.

Connect — paste your key

  1. Dashboard URL: your Sums Solution dashboard address, subfolder included (https://sumssolution.com/app) — never your WordPress site. The plugin posts to /api/v1/plugin/findings on it.
  2. Site API key: copy it from Dashboard → Sites → your site → API key.
  3. Save settings, then click Test connection — a green "Connected!" notice means pairing succeeded.
  4. Toggle the protections you want: file integrity, daily malware scan, login monitor, real-time guard.
  5. After a deliberate WordPress core update, click "Rebuild file baseline" so intentional changes stop alerting.

Requirements

  • WordPress 6.0 or newer
  • PHP 8.0 or newer
  • A Sums Solution account with a registered site, an API key and the Starter plan or above (the connector syncs on Starter, Business and Agency)
  • Outbound HTTPS access to your dashboard domain

Need help?

The knowledge base covers 99 entries — threats, attack techniques and malware guides — and if something is unclear, email us and we will answer.

Open knowledge base

We use only the cookies needed to run this site — your session, your sign-in state and CSRF protection. There are no advertising or analytics trackers. How cookies are used