@hypercerts-org/lexicon
ATProto lexicon definitions and TypeScript types for the
Hypercerts protocol — a system for tracking,
evaluating, and funding impact work on the
AT Protocol network.
Lexicon Map
CLAIMS ─ the core impact record and its parts
──────────────────────────────────────────────────────────────────────
activity ──────────┬──► collection ◄──┐ (recursive nesting)
(the hypercert) │ │ │
│ ▼ │
├──► contribution (role, timeframe)
├──► contributorInformation (identity, image)
├──► rights (licensing terms)
└──► workScope
├── cel ───► tag (CEL expression referencing tags)
└── string (free-form scope)
CONTEXT ─ evidence, data, and social verification
──────────────────────────────────────────────────────────────────────
attachment ─────────────► any record (activity, evaluation, …)
measurement ────────────► any record (activity, …)
evaluation ─────────────► any record (activity, measurement, …)
└──────► measurement
acknowledgement ────────► any record (bidirectional link)
FUNDING ─ payment records
──────────────────────────────────────────────────────────────────────
receipt ────────────────► activity (from funder → to recipient)
HYPERBOARDS ─ visual display layer (hyperboards.org)
──────────────────────────────────────────────────────────────────────
board ──────────────────► activity / collection
└── contributorConfig ► contributorInformation
displayProfile (per-user visual defaults)
CERTIFIED ─ shared lexicons (certified.app)
──────────────────────────────────────────────────────────────────────
location (geo coordinates, GeoJSON, H3, …)
link/evm (ATProto DID ↔ EVM wallet link)
actor/profile (user profile)
actor/organization (org metadata)
badge/response ──► badge/award ──► badge/definition
graph/follow ────────────► account DID (social follow)
graph/entityFollow ──────► record (by AT-URI) (non-account follow)
signature/defs (shared #list and #inline defs)
signature/proof (remote attestation proof record)
Every arrow (►) is a strongRef or union reference stored on the
AT Protocol network. Full field-level documentation is in
SCHEMAS.md.
Consuming These Lexicons
If you are building a downstream application on top of these lexicons,
we strongly recommend NOT reading from main or other development
branches of the repository, but instead via the following published
releases:
Both npm releases and git tags follow SemVer.
For npm, you can depend on a version range to receive compatible
updates automatically. For GitHub releases/tags, pin a specific tag
or upgrade manually to a newer compatible SemVer release.
The raw lexicons published on ATProto can also be used, but they are
(unavoidably) missing useful context such as full documentation
(including changelogs), TypeScript type definitions, SemVer
guarantees, git history, and other tooling provided by the packaged
releases.
AI Agent Skill
If you use AI coding assistants (e.g. Claude Code, OpenCode), you can
install a skill that teaches your agent how to build with these
lexicons:
npx skills add hypercerts-org/hypercerts-lexicon
This installs the
building-with-hypercerts-lexicons
skill, which provides your agent with guidance on package entry points,
TypeScript types, validation, all lexicon schemas, code examples, and
AT Protocol conventions.
Maintenance and publishing releases
Clearly stability and predictability for users and developers are
essential.
Unfortunately AT Protocol doesn't support any kind of native
versioning or migrations which could support lexicon schema changes.
Instead, the AT Protocol community recommends minimising changes to
lexicons in general, and to avoid breaking changes wherever possible:
This project intends to follow that guidance as much as possible
whilst retaining a pragmatic approach. In practice that means:
-
Changes to other tooling within this repository which do not touch
lexicons may be made at any time as long as they follow
SemVer to avoid negative impact on
developers.
-
Non-breaking changes to lexicons, such as adding an optional
property or updating a description, may be made sparingly. While
these changes are backwards-compatible at the protocol level, they
may still require consuming applications and indexers to update
their schemas for consistent UX.
-
Breaking changes to lexicons will only be made in exceptional
circumstances. Specifically, a breaking change will only proceed
if and only if:
- the broader community — not just the Hypercerts core team —
agrees that the benefits clearly outweigh the cost of the
breakage, and
- full consideration is given to all affected parties across the
community and wider ecosystem, not only those involved in the
decision, and
- no viable alternative exists, such as releasing a new
.v2
version of the lexicon or introducing a v2 field.
To date, breaking changes have only occurred during the early
stages of launching Hypercerts on AT Protocol, before external
consumers were building against the lexicons. We intend to keep
it that way.
It is also worth noting that members of the ATProto community have
been working on tooling to make these problems easier to deal with in
future, e.g. see https://panproto.dev/
Use of branches
main is the only evergreen branch and the default branch on GitHub.
We aim to minimise deviations between main and versions published on
npm and ATProto. However the publishing processes involve several
moving parts (including third-party systems), and it is technically
impossible to update all three at the same time. So please do not
assume they will always be perfectly in sync.
See docs/PUBLISHING.md for the full release workflow.
If you see a develop branch, it is a stale leftover from a
previous workflow and is no longer used; do not open pull requests
against it.
Contributing / development
Please see CONTRIBUTING.md.
Project Structure
lexicons/ Source of truth (committed)
org/hypercerts/ Hypercerts protocol lexicons
org/hyperboards/ Hyperboards visual layer lexicons
app/certified/ Shared/certified lexicons
com/atproto/ ATProto external references
generated/ Auto-generated TypeScript (gitignored)
dist/ Built bundles (gitignored)
scripts/ Build and codegen scripts
Never edit generated/ or dist/ directly — they are
regenerated from lexicon JSON files.
Installation
npm install @hypercerts-org/lexicon
Quick Start
import {
HYPERCERTS_SCHEMAS,
ACTIVITY_NSID,
validate,
} from "@hypercerts-org/lexicon";
import { Agent } from "@atproto/api";
const agent = new Agent({ service: "https://bsky.social" });
agent.api.lex.add(...HYPERCERTS_SCHEMAS);
const record = {
$type: ACTIVITY_NSID,
title: "Reforestation in Amazon Basin 2024",
shortDescription: "Planted 5,000 native trees across 12 hectares",
createdAt: new Date().toISOString(),
};
const result = validate(record, ACTIVITY_NSID, "main");
if (!result.success) throw new Error(String(result.error));
await agent.api.com.atproto.repo.createRecord({
repo: agent.session?.did,
collection: ACTIVITY_NSID,
record,
});
Lexicon Reference
Claims (org.hypercerts.claim.*)
| Activity | org.hypercerts.claim.activity | The main hypercert record — describes impact work with title, description, contributors, work scope, timeframe, locations, and rights. |
| Contribution | org.hypercerts.claim.contribution | Details about a specific contribution: role, description, and timeframe. |
| Contributor Information | org.hypercerts.claim.contributorInformation | Identity record for a contributor: identifier (DID or URI), display name, and image. |
| Rights | org.hypercerts.claim.rights | Licensing and rights terms (e.g. "CC BY-SA 4.0") attached to an activity. |
Collections (org.hypercerts.*)
| Collection | org.hypercerts.collection | A named, weighted group of activities and/or other collections. Supports recursive nesting. Used for projects, portfolios, favourites, funding rounds, etc. |
Context (org.hypercerts.context.*)
| Attachment | org.hypercerts.context.attachment | Documents, reports, evidence, or other files linked to a record. |
| Measurement | org.hypercerts.context.measurement | Quantitative data point (metric + unit + value) linked to one or more records. |
| Evaluation | org.hypercerts.context.evaluation | An assessment of a record with evaluators, summary, score, and supporting measurements. |
| Acknowledgement | org.hypercerts.context.acknowledgement | Bidirectional link: confirms or rejects inclusion of a record in another context. |
Work Scope (org.hypercerts.workscope.*)
| Tag | org.hypercerts.workscope.tag | Reusable scope atom (topic, domain, method, …) with taxonomy support, aliases, and linked ontologies. |
| CEL Expression | org.hypercerts.workscope.cel | Structured work scope using CEL expressions over tags. Embedded inline in activity records. |
Funding (org.hypercerts.funding.*)
| Receipt | org.hypercerts.funding.receipt | Records a payment to a recipient, with amount, currency, payment rail, and optional transaction ID. The sender (from) is optional to support anonymous funders. |
Hyperboards (org.hyperboards.*)
| Board | org.hyperboards.board | Visual presentation layer wrapping an activity or collection with background, colors, aspect ratio, and per-contributor styling. |
| Display Profile | org.hyperboards.displayProfile | Per-user visual defaults (avatar, hover image, video, click-through URL) reusable across boards. Singleton record (literal:self). |
Certified (app.certified.*)
| Location | app.certified.location | Geographic reference using the Location Protocol (coordinates, GeoJSON, H3, WKT, etc.). |
| Profile | app.certified.actor.profile | User account profile with display name, bio, avatar, and banner. |
| Organization | app.certified.actor.organization | Organization metadata: legal structure, URLs, location, founding date, optional long description, and discoverability visibility. |
| Badge Definition | app.certified.badge.definition | Defines a badge type with title, icon, and optional issuer allowlist. |
| Badge Award | app.certified.badge.award | Awards a badge to a user, project, or activity. |
| Badge Response | app.certified.badge.response | Recipient accepts or rejects a badge award. |
| EVM Link | app.certified.link.evm | Verifiable ATProto DID ↔ EVM wallet link via EIP-712 signature. Extensible for future proof methods (e.g. ERC-1271, ERC-6492). |
| Follow | app.certified.graph.follow | Social-graph follow relationship — declares that the author follows another account by DID. Schema-compatible with app.bsky.graph.follow. |
| Entity Follow | app.certified.graph.entityFollow | Social-graph follow relationship for non-account entities (e.g. a record, referenced by AT-URI without a CID so the follow survives updates). Account (DID) follows remain in app.certified.graph.follow; subject is an open union so future non-DID entity kinds can be added non-breakingly. |
Signatures (app.certified.signature.*)
| Shared Defs | app.certified.signature.defs | Shared type definitions for signatures. Provides the #list array def (a union of inline signatures and strongRefs) and the #inline object def (the inline signature shape), all referenced by the signatures property on records. |
| Proof | app.certified.signature.proof | Remote attestation proof record containing the CID of attested content. Lives in the attestor's repository and can be referenced via strongRef. |
All record lexicons include an optional signatures property (a ref to app.certified.signature.defs#list) enabling cryptographic attestations. See Cryptographic Signatures for usage details.
Note on app.certified.link.evm: it carries two orthogonal integrity primitives. The EIP-712 proof field proves wallet consent (the EVM key holder agreed to be linked to the DID). The signatures array proves record provenance (e.g. that a platform UI minted the record, defending against replay of harvested EIP-712 signatures). Both are useful and they do not conflict.
Full property tables → SCHEMAS.md
Schema Conventions
A few conventions are worth knowing before you start emitting records,
because they affect what counts as a "valid" record vs. one that breaks
interop with the wider ecosystem.
knownValues is an open vocabulary, not a closed enum
When you see "knownValues": [...] on a string field, those values are
conventions for interoperability, not constraints. Lexicon validators
do not reject values outside the list. The vocabulary is intentionally
open so applications can extend it with domain-specific variants without
waiting for a schema bump.
If the lexicon authors wanted a field to be closed, they would have used
enum instead — which the validator does enforce.
In practice:
- Prefer a
knownValues entry when one matches your semantics. It
guarantees other Hypercerts-aware consumers (indexers, AppViews,
search facets, dropdown UIs) will recognize the value without
per-app special-casing.
- Custom values are permitted when no listed value fits. The record
is still wire-valid. Consumers that filter on the canonical list
simply won't categorize the value; consumers that read the raw string
will see whatever you wrote.
- Custom values are at-your-own-risk for interop. A custom
locationType: "geojson-polygon", for example, is a perfectly valid
record — but a downstream tool that buckets by the Location Protocol
registry will treat it as "other". Use the closest knownValues
entry (here, geojson, which is the catch-all for non-Point GeoJSON)
when you want canonical bucketing.
When a knownValues list grows in a way that's broadly useful, open a
PR to add the new entry to the lexicon — that's how the canonical list
stays in sync with real-world usage.
strongRef pins to a specific record version
Fields typed as com.atproto.repo.strongRef carry both uri AND cid.
The cid is a content hash that pins the reference to the exact record
version at the time the reference was written — if the referenced
record is later overwritten, consumers can detect that the reference
has drifted.
This matters when you might otherwise be tempted to embed a full record
by value to "snapshot" it. A strongRef + CID already provides the
snapshot semantic, and it's what the lexicon uses across
badge.award.badge, badge.response.badgeAward, funding.receipt.for,
and similar fields where the historical content needs to stay stable.
Entity Relationship Diagram

View ERD with field details

Usage
Accessing NSIDs
Individual constants (recommended):
import {
ACTIVITY_NSID,
HYPERCERTS_COLLECTION_NSID,
} from "@hypercerts-org/lexicon";
Semantic object:
import { HYPERCERTS_NSIDS } from "@hypercerts-org/lexicon";
const id = HYPERCERTS_NSIDS.ACTIVITY;
Type-based mapping:
import { HYPERCERTS_NSIDS_BY_TYPE } from "@hypercerts-org/lexicon";
const id = HYPERCERTS_NSIDS_BY_TYPE.OrgHypercertsClaimActivity;
Lightweight bundle (no TypeScript types, smaller bundle):
import { schemas, validate, ids } from "@hypercerts-org/lexicon/lexicons";
TypeScript Types
import { OrgHypercertsClaimActivity } from "@hypercerts-org/lexicon";
const activity: OrgHypercertsClaimActivity.Main = {
$type: "org.hypercerts.claim.activity",
title: "My Impact Work",
shortDescription: "...",
createdAt: new Date().toISOString(),
};
Lexicon Documents
import {
ACTIVITY_LEXICON_JSON,
ACTIVITY_LEXICON_DOC,
} from "@hypercerts-org/lexicon";
Or via semantic mapping objects:
import {
HYPERCERTS_LEXICON_JSON,
HYPERCERTS_LEXICON_DOC,
} from "@hypercerts-org/lexicon";
const doc = HYPERCERTS_LEXICON_DOC.ACTIVITY;
Examples
Creating Activities with Work Scope
import { ACTIVITY_NSID } from "@hypercerts-org/lexicon";
const activity = {
$type: ACTIVITY_NSID,
title: "Mangrove Restoration in Mombasa",
shortDescription: "Restored 3 hectares of mangrove forest",
workScope: {
$type: "org.hypercerts.workscope.cel",
expression:
"scope.hasAll(['mangrove_restoration']) && location.country == 'KE'",
usedTags: [
{
uri: "at://did:plc:alice/org.hypercerts.workscope.tag/3k2abc",
cid: "...",
},
],
version: "v1",
createdAt: new Date().toISOString(),
},
startDate: "2024-01-01T00:00:00Z",
endDate: "2024-12-31T23:59:59Z",
createdAt: new Date().toISOString(),
};
Creating Collections (Projects, Portfolios, etc.)
import { HYPERCERTS_COLLECTION_NSID } from "@hypercerts-org/lexicon";
const project = {
$type: HYPERCERTS_COLLECTION_NSID,
type: "project",
title: "Carbon Offset Initiative",
shortDescription: "Activities focused on carbon reduction and reforestation",
items: [
{
itemIdentifier: {
uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc",
cid: "...",
},
},
{
itemIdentifier: {
uri: "at://did:plc:bob/org.hypercerts.claim.activity/7x9def",
cid: "...",
},
},
{
itemIdentifier: {
uri: "at://did:plc:carol/org.hypercerts.collection/4m5ghi",
cid: "...",
},
},
],
createdAt: new Date().toISOString(),
};
Creating Location Records
import { LOCATION_NSID } from "@hypercerts-org/lexicon";
const location = {
$type: LOCATION_NSID,
lpVersion: "1.0",
srs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
locationType: "coordinate-decimal",
location: {
$type: "app.certified.location#string",
string: "-3.4653, -62.2159",
},
name: "Amazon Research Station",
createdAt: new Date().toISOString(),
};
const geoLocation = {
$type: LOCATION_NSID,
lpVersion: "1.0",
srs: "http://www.opengis.net/def/crs/OGC/1.3/CRS84",
locationType: "geojson-point",
location: {
$type: "app.certified.location#string",
string: '{"type":"Point","coordinates":[-62.2159,-3.4653]}',
},
name: "Research Station Alpha",
createdAt: new Date().toISOString(),
};
Acknowledging Inclusion
When one user includes another's record (e.g. adding an activity to a
collection), the owner can confirm or reject with an acknowledgement:
import { CONTEXT_ACKNOWLEDGEMENT_NSID } from "@hypercerts-org/lexicon";
const ack = {
$type: CONTEXT_ACKNOWLEDGEMENT_NSID,
subject: {
uri: "at://did:plc:bob/org.hypercerts.claim.activity/3k2abc",
cid: "bafy...",
},
context: {
$type: "com.atproto.repo.strongRef",
uri: "at://did:plc:alice/org.hypercerts.collection/7x9def",
cid: "bafy...",
},
acknowledged: true,
createdAt: new Date().toISOString(),
};
Creating Attachments
import { CONTEXT_ATTACHMENT_NSID } from "@hypercerts-org/lexicon";
const attachment = {
$type: CONTEXT_ATTACHMENT_NSID,
title: "Field Survey Report",
contentType: "report",
subjects: [
{
uri: "at://did:plc:alice/org.hypercerts.claim.activity/abc123",
cid: "...",
},
],
content: [
{
$type: "org.hypercerts.defs#uri",
uri: "https://example.com/reports/survey-2024.pdf",
},
{ $type: "org.hypercerts.defs#uri", uri: "ipfs://Qm..." },
],
shortDescription: "Quarterly field survey documenting project progress",
createdAt: new Date().toISOString(),
};
Cryptographic Signatures
All record lexicons support optional cryptographic signatures via the signatures property. This enables platform attestation and verification that records were created through trusted services.
The on-the-wire shape, signing procedure, and verification procedure all conform to Nick Gerakines' ATProtocol Attestation Specification (see also the accompanying blog post). In particular, signatures sign the CID of the record (not its raw bytes), and CID generation injects a temporary $sig object carrying the housing repository's DID so that signatures cannot be replayed across content versions or across repositories.
Two patterns are supported:
- Inline signatures: Embedded directly in the record via
app.certified.signature.defs#inline.
- Remote attestations: References to
app.certified.signature.proof records in other repositories, via com.atproto.repo.strongRef.
Signing Algorithm
The spec mandates ECDSA with the low-S variant per BIP-0062. The signing curve is determined by the multicodec prefix of the verification method's publicKeyMultibase in the signer's DID document:
0xE701 | secp256k1 (K-256) | ES256K |
0x1200 | P-256 (secp256r1) | ES256 |
There is deliberately no signatureType field on the inline signature: the algorithm is canonically derived from the resolved key, and a separate tag would risk disagreeing with the multicodec.
Key management. The platform signing keypair is a long-lived identity, not a per-record artefact. It MUST be generated once, persisted in secure storage (HSM, KMS, sealed secret, etc.), and reused across every signed record so that the public key resolved from the key DID-URL stays stable for verifiers. Generating a fresh keypair per signature (the "throwaway key" anti-pattern) defeats verification: nobody can confirm that two records came from the same signer, and the did:key: in key will not match anything any verifier has previously seen or pinned.
Step 1: Load (or Create) the Platform Signing Keypair
Use exportable: true so the private key bytes can be written to your secret store, then reload them on every subsequent process start. The example below performs an in-memory round trip via export() / import() to demonstrate the API surface — in production, replace the round trip with real reads and writes against your KMS / sealed-secret store:
import { Secp256k1Keypair } from "@atproto/crypto";
const fresh = await Secp256k1Keypair.create({ exportable: true });
const privateKeyBytes = await fresh.export();
const keypair = await Secp256k1Keypair.import(privateKeyBytes);
Step 2: Build an Inline Signature
Compute the spec-defined CID over the record-to-be-signed, then ECDSA-sign it with the platform's key. Uses @atproto/crypto (ATProto's signing primitives — handles low-S automatically), @ipld/dag-cbor for canonical encoding, and multiformats for CID construction:
import * as dagCbor from "@ipld/dag-cbor";
import { CID } from "multiformats/cid";
import { sha256 } from "multiformats/hashes/sha2";
import { ACTIVITY_NSID } from "@hypercerts-org/lexicon";
const recordToSign = {
$type: ACTIVITY_NSID,
title: "Verified Reforestation Project",
shortDescription: "Planted 1,000 trees in partnership with local community",
createdAt: "2026-05-21T12:00:00.000Z",
};
const platformDid = "did:plc:platform123";
const cborInput = {
...recordToSign,
$sig: {
$type: "app.certified.signature.defs#inline",
repository: platformDid,
},
};
const cborBytes = dagCbor.encode(cborInput);
const hash = await sha256.digest(cborBytes);
const cid = CID.createV1(dagCbor.code, hash);
const cidBytes = cid.bytes;
const signerDid = keypair.did();
if (!signerDid.startsWith("did:key:")) {
throw new Error(`Expected did:key signer, got ${signerDid}`);
}
const signerKey = `${signerDid}#${signerDid.slice("did:key:".length)}`;
const signatureBytes = await keypair.sign(cidBytes);
const inlineSignature = {
$type: "app.certified.signature.defs#inline" as const,
signature: signatureBytes,
key: signerKey,
};
Step 3: Build a Remote Attestation Reference (Optional)
Only if the attestation lives in another repo's proof record:
const remoteAttestation = {
$type: "com.atproto.repo.strongRef" as const,
uri: "at://did:plc:verifier/app.certified.signature.proof/abc123",
cid: "bafy...",
};
Step 4: Attach to the Record
Both shapes can coexist in the same array; consumers verify each entry independently.
const signedActivity = {
...recordToSign,
signatures: [inlineSignature, remoteAttestation],
};
Verifying a Signature
To verify, reconstruct the same CID using the housing repo's DID, resolve key to a public key via the DID document, and check the ECDSA signature against the CID bytes.
Remote Attestation Proofs
For remote attestations, create a proof record in the attestor's repository:
import { SIGNATURE_PROOF_NSID } from "@hypercerts-org/lexicon";
const proof = {
$type: SIGNATURE_PROOF_NSID,
cid: "bafy...",
note: "Verified by platform quality assurance process",
createdAt: new Date().toISOString(),
};
For the full specification, see Nick Gerakines' ATProtocol Attestation Specification. For background and motivation, see the accompanying blog post.
Development
Commands
npm run gen-api
npm run build
npm run check
npm run lint
npm run format
npm run gen-schemas-md
npm run test
Following another account
The app.certified.graph.follow record is the social-graph primitive
for certified.app. Its shape is identical to app.bsky.graph.follow
(same key: tid, same subject / createdAt / optional via
fields), so feed-builders and view services can index it with the
same logic they already use for Bluesky follows.
import { GRAPH_FOLLOW_NSID } from "@hypercerts-org/lexicon";
const follow = {
$type: GRAPH_FOLLOW_NSID,
subject: "did:plc:ewvi7nxzyoun6zhxrhs64oiz",
createdAt: new Date().toISOString(),
};
The optional via field is a com.atproto.repo.strongRef to any
record that mediated the follow (e.g. a starter pack or other curated
list), mirroring the equivalent field on app.bsky.graph.follow.
Following a non-account entity
app.certified.graph.entityFollow is the sibling of
app.certified.graph.follow for following anything that is not an
account. Account (DID) follows are explicitly out of scope here — use
app.certified.graph.follow for those. Today the only subject variant
is app.certified.defs#recordSubject, a record referenced by AT-URI
(DID form, no CID), so the reference survives later updates to the
record. subject is an open union so additional non-DID entity kinds
can be added in the future without a breaking change.
import { GRAPH_ENTITY_FOLLOW_NSID } from "@hypercerts-org/lexicon";
const entityFollow = {
$type: GRAPH_ENTITY_FOLLOW_NSID,
subject: {
$type: "app.certified.defs#recordSubject",
uri: "at://did:plc:alice/org.hypercerts.claim.activity/3k2abc",
},
createdAt: new Date().toISOString(),
};
Linking ATProto Identity to EVM Wallets
The app.certified.link.evm record enables verifiable linking between
an ATProto DID and an EVM wallet address. The link is proven via a
cryptographic signature, allowing any verifier to confirm that the
wallet owner authorized the binding. Currently supports EOA wallets
via EIP-712 typed data signatures; the proof field is an open union
to allow future signature methods (e.g. ERC-1271, ERC-6492).
import { LINK_EVM_NSID } from "@hypercerts-org/lexicon";
const evmLinkRecord = {
$type: LINK_EVM_NSID,
address: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
proof: {
$type: "app.certified.link.evm#eip712Proof",
signature: "0xabc123...",
message: {
$type: "app.certified.link.evm#eip712Message",
did: "did:plc:alice",
evmAddress: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
chainId: "1",
timestamp: "1709500000",
nonce: "0",
},
},
createdAt: new Date().toISOString(),
};
Key fields:
address (required): 0x-prefixed EVM wallet address (EIP-55
checksummed, 42 chars)
proof (required): Open union containing the cryptographic proof of
wallet ownership. Each variant bundles its signature with the
corresponding message format. Currently the only variant is
#eip712Proof for EOA wallets.
createdAt (required): Timestamp when the record was created
License
MIT