propgate docs

SDKprofiles

Profiles

A profile is what you expect of a domain's records. Domains pin the version they were registered against, so a profile is append-only from the outside: there is no update method, and writing a key that exists creates a new version.

const { data, error } = await propgate.profiles.create({
  key: "sending",
  requirements: [
    { key: "ns", check: "delegation" },
    { key: "spf", check: "spf", include: "_spf.google.com" },
    { key: "dkim", check: "dkim", selector: "google" },
    { key: "dmarc", check: "dmarc" },
    { key: "mail", check: "mx", expectsMail: true },
  ],
});

data?.version; // 1 the first time, 2 the next time you write this key

Existing domains keep being judged by the version they hold until something re-points them — see domains.update. That is the whole reason versions exist: editing a profile in place would silently re-judge every domain on it, and a customer who was verified on Tuesday would fail on Wednesday without their DNS having changed.

See Profiles and versions for the model, and create a profile for every field a requirement takes.

Values the domain supplies

A profile states the shape; a domain can supply the value. Name a field in requiredPerDomain and every domain registered against the profile must send it in expectations:

await propgate.profiles.create({
  key: "sending",
  requirements: [
    {
      key: "dkim",
      check: "dkim",
      selector: "pg1",
      // The value is issued per customer, so the domain supplies it.
      requiredPerDomain: ["expectedPublicKey"],
    },
    // Two names, one profile: SPF and MX at a bounce host beneath the domain.
    { key: "bounce-spf", check: "spf", label: "send", include: "spf.acme.com" },
    { key: "bounce-mx", check: "mx", label: "send", expectsMail: true },
    { key: "apex-mx", check: "mx", expectsMail: false },
  ],
});

Without this a platform with ten thousand customers needs ten thousand profiles and the versioning stops meaning anything. label is what lets one profile describe two names — the domain itself and a bounce host beneath it — so a customer's setup is one registered domain and one state rather than two.

A domain registered without a required value is refused at write time, naming the path. See register a domain.

Reading one back

propgate.profiles.get() returns the current version, which is not necessarily the one a given domain is pinned to. domain.profileVersionId is the id that was in force when it was registered.

const { data, error } = await propgate.profiles.get("sending");

if (error?.code === "not_found") {
  // No profile by that key on this account.
}

Typed requirements

import type { ProfileRequirement } from "@propgate/sdk";

// `check` is the same union the evaluators use, so a typo is a compile error
// rather than a 422 you find at runtime.
const dmarc: ProfileRequirement = { key: "dmarc", check: "dmarc" };

ProfileRequirement["check"] is CheckKind from @propgate/dns — the same union the evaluators are written against. A check kind that does not exist fails tsc rather than the request.