Schnittstellen

REST-Referenz

Alle Endpunkte der Core-API. Die Pfade sind relativ zu dieser Seite: Sie leitet /api und /v1 an den Kern weiter, also nutzen Browser-Sitzung und Skript genau dieselben URLs.

Konto

Registrieren, Anmelden, Passwort und Profil. Läuft über Better Auth.

POST/api/auth/sign-up/emailOffen

Creates an account and sends the confirmation e-mail.

Body
email, password (at least 10 characters), name
Antwortet
the new user and a session
Limit
5 per hour and IP address

POST/api/auth/sign-in/emailOffen

Signs in with an e-mail address.

Body
email, password
Antwortet
a session; the token is also in the set-auth-token header for callers without cookies
Limit
5 per minute and IP address

POST/api/auth/sign-in/usernameOffen

Signs in with a username instead of an e-mail address.

Body
username, password
Antwortet
a session
Limit
5 per minute and IP address

POST/api/auth/sign-outMensch

Ends the current session.

Antwortet
{ success: true }

POST/api/auth/update-userMensch

Changes the profile: display name, username and whether Deeds may show the name.

Body
any of name, username, defaultVisibility
Antwortet
the updated user

POST/api/auth/request-password-resetOffen

Sends a password reset link. Answers the same way whether or not the address exists.

Body
email, redirectTo
Antwortet
{ status: true }

POST/api/auth/reset-passwordOffen

Sets a new password and ends all other sessions of that account.

Body
token from the e-mail link, newPassword
Antwortet
{ status: true }

GET/api/auth/verify-emailOffen

Confirms an e-mail address from the link in the message.

Query
?token the token from the e-mail link (Pflicht)
Antwortet
a redirect back to the website

Öffentlich

Alles, was jeder lesen darf: Regeln, Geld, Katalog, Deeds mit Belegen, Statistik, Karte.

GET/v1/public/rulesOffen

The earning rules that are active right now.

Antwortet
{ rules: [...] } with reward, unit, daily cap, the minimum trust level and audience; invited-only rules add invitedCount (never names)

GET/v1/public/changelogOffen

Every rule version with its reason, plus every budget deposit. Nothing is ever changed in place.

Antwortet
{ ruleVersions: [...], budgetDeposits: [...] }

GET/v1/public/budgetOffen

Where the money behind the Drops is: deposited, not yet issued, in wallets, reserved, donated.

Antwortet
amounts in milli-units as decimal strings

GET/v1/public/catalogOffen

The redemption catalog by category, with current prices and pool state.

Antwortet
{ categories: [...], providers: [...] }

GET/v1/public/catalog/:slugOffen

One project with its complete price history.

Pfad
:slug project slug, e.g. ecologi-tree
Antwortet
the project, its provider, pool and priceHistory

GET/v1/public/deedsOffen

All settled payouts. Contributors who chose anonymity have name: null.

Antwortet
{ deeds: [...] } newest first

GET/v1/public/deeds/:idOffen

One Deed with its proof: the snapshot frozen at payout time, the ledger posting and a fresh check of the receipt hash.

Pfad
:id payout id (UUID)
Antwortet
the Deed plus receiptCheck: matches, differs or missing

GET/v1/public/payouts/:id/receiptOffen

The receipt file of a payout, so anyone can hash it themselves.

Pfad
:id payout id (UUID)
Antwortet
the file as it was uploaded
Kommt zurück als
application/pdf, image/png, image/jpeg or image/webp

GET/v1/public/impactOffen

The headline totals: donated and queued Drops, Deeds, contributors.

Antwortet
one object with four numbers

GET/v1/public/statsOffen

What the Deeds financed per project, with CO₂e grouped by period, impact type and how well the figure is backed. Living units are counted from the day they were paid for, so the totals grow on their own.

Antwortet
{ since, deeds, contributors, financed: [...], co2e: { groups, dropsWithoutFactor } }; each group carries accumulated and perYear

GET/v1/public/mapOffen

Activity per country for the world map. Never finer than a country.

Query
?days window of 1 to 90 days, 7 by default
Antwortet
{ days, countries: [{ country, activities, people }] }

GET/v1/public/footprintOffen

The server's estimated CO₂e and water use as reported by the hosting provider, the website's traffic, and what the Deeds dealt with in the same days. They stand side by side; nothing is offset or subtracted.

Query
?period day, week, month (default) or year: a rolling window ending on the last day the provider has figures for
Antwortet
{ period, since, window: { from, to, days, bucket }, server: { co2eGrams, waterLitres, daysWithData }, visits: { bytes, responses, daysWithData, since }, buckets: [{ start, days, co2eGrams, waterLitres, bytes, uptake }], deeds: [{ period, impactType, basis, units, amount, sourceUrls }] }; window and server are null before the first figures arrive, visits before the website reported traffic; uptake is the estimated daily uptake of living units, never subtracted from anything

Dein Konto

Deine Daten, Einlösungen, Geräte und dein Recht, alles zu exportieren oder zu löschen.

GET/v1/meMensch

The signed-in person with their Drops and Sparks balance.

Antwortet
{ user, balances }

GET/v1/me/activitiesMensch

Your own reported activities and what they earned.

Antwortet
{ activities: [...] }

POST/v1/me/activitiesMensch

Reports an activity yourself. Trust level self_reported, so most rules will not pay for it.

Body
source, type, quantity, unit, startedAt, endedAt, optional externalId and zone
Antwortet
the activity and the reward decision (Status 201)

GET/v1/me/statsMensch

Your own share: what your Drops financed and the CO₂e estimate behind it, counted from the day each Deed was paid.

Antwortet
the same shape as /v1/public/stats, limited to your redemptions

GET/v1/me/invitationsMensch

The rules you are invited to. Rules with audience invited pay only invited people; being invited also lets you confirm your device's link code.

Antwortet
{ invitations: [{ ruleId, key, title, source, translations, createdAt }] }

GET/v1/me/redemptionsMensch

Your redemptions with their status.

Antwortet
{ redemptions: [...] }

POST/v1/me/redemptionsMensch

Turns Drops into a queued donation; the Drops are reserved at today's price.

Body
projectId plus quantity (unit projects) or drops (free amount), optional visibility
Antwortet
the redemption (Status 201)

POST/v1/me/redemptions/:id/cancelMensch

Takes a redemption back while it is still queued and returns the Drops.

Pfad
:id redemption id (UUID)
Antwortet
the cancelled redemption

GET/v1/me/clientsMensch

Your connected apps and devices.

Antwortet
{ clients: [...] } without key hashes

POST/v1/me/clientsMensch

Creates an API key for an app. The key is shown once and only its hash is stored. Only admins may do this for now (403 clients_owner_only otherwise); a key made by anyone else counts as a third-party app.

Body
name, optional kind (first_party for admins), optional signingPublicKey (Ed25519, base64)
Antwortet
the client and apiKey (Status 201)
Limit
10 per hour and account

DELETE/v1/me/clients/:idMensch

Revokes a device: its key stops working immediately.

Pfad
:id client id (UUID)
Antwortet
nothing (Status 204)

POST/v1/me/link/:code/approveMensch

Approves a device. It may then report activities for you, nothing else. Only admins may do this for now (403 clients_owner_only).

Pfad
:code the code shown on the device
Antwortet
nothing (Status 204)

POST/v1/me/link/:code/denyMensch

Refuses a link request.

Pfad
:code the code shown on the device
Antwortet
nothing (Status 204)

GET/v1/me/exportMensch

Your data as one JSON file (GDPR art. 15 and 20). Contains no password hash, token or key hash.

Antwortet
a download with profile, devices, activities, rewards, redemptions and postings
Kommt zurück als
application/json

POST/v1/me/deleteMensch

Deletes your account by anonymising it: the ledger and the Deeds stay, everything that points at you goes. Queued redemptions are donated anonymously, the rest of the Drops goes back to the budget.

Body
password
Antwortet
nothing (Status 204)
Limit
10 per hour and account

Apps und Geräte

Die Maschinenseite: Aktivitäten mit API-Schlüssel melden.

POST/v1/client/activitiesApp oder Gerät

Reports one activity. externalId makes it idempotent, so a retry never pays twice. With an Ed25519 signature in X-Elpia-Signature the activity counts as verified, otherwise as device.

Body
source, type, quantity, unit, startedAt, endedAt, externalId, optional zone, energyWhMeasured, energyWhEstimated
Antwortet
the activity and the reward decision (Status 201)
Limit
600 per minute and API key

GET/v1/client/meApp oder Gerät

What this key reported and earned (last 24 hours and in total), the latest reward decision and the account's balances. Nothing about other apps.

Antwortet
{ client, last24h, total, lastRewardStatus, balances }, amounts in milli units
Limit
600 per minute and API key

DELETE/v1/client/meApp oder Gerät

Revokes this key, e.g. when someone logs out on the device. It stops working immediately and cannot be restored.

Antwortet
nothing (Status 204)
Limit
600 per minute and API key

Inhaber

Regeln, Budget, Katalog, Auszahlungen und die Integritätsprüfung.

GET/v1/admin/auditInhaber

Checks the books: balanced postings, no unexpected negative balances, redemptions matching payouts, receipts matching their hash.

Antwortet
the report; 409 instead of 200 when there are findings

GET/v1/admin/rulesInhaber

All rule versions, including the disabled ones.

Antwortet
{ versions: [...] }

POST/v1/admin/rulesInhaber

Creates a rule with its first version.

Body
key and a version object with reward, unit, caps, minimum trust and a public reason
Antwortet
the rule and its version (Status 201)

POST/v1/admin/rules/:id/versionsInhaber

Adds a new version of a rule. The old one stays readable; nothing is ever rewritten.

Pfad
:id rule id (UUID)
Body
the new version with a public reason
Antwortet
the new version (Status 201)

GET/v1/admin/rules/:id/invitationsInhaber

Who is invited to a rule that only pays invited people.

Pfad
:id rule id (UUID)
Antwortet
{ invitations: [{ userId, username, email, createdAt }] }

POST/v1/admin/rules/:id/invitationsInhaber

Invites a person to a rule. Inviting someone twice changes nothing; an unknown person gives 404 user_not_found.

Pfad
:id rule id (UUID)
Body
user: username or e-mail address
Antwortet
the invited person (Status 201)

DELETE/v1/admin/rules/:id/invitations/:userIdInhaber

Removes an invitation. New activities stop earning; rewards already booked stay.

Pfad
:id rule id (UUID)
Pfad
:userId the invited person's id
Antwortet
nothing (Status 204)

POST/v1/admin/budget/depositsInhaber

Pays money into the budget. Drops can only be issued from it.

Body
amountCents, optional memo
Antwortet
the ledger transaction (Status 201)

GET/v1/admin/catalogInhaber

The catalog including inactive providers and projects.

Antwortet
like the public catalog, but complete

POST/v1/admin/categoriesInhaber

Creates a category.

Body
slug, name, optional description, translations, sortOrder
Antwortet
the category (Status 201)

PATCH/v1/admin/categories/:idInhaber

Changes a category. Only the fields that are sent change.

Pfad
:id category id (UUID)
Body
any subset of the fields
Antwortet
the category

POST/v1/admin/providersInhaber

Creates a provider with its minimum payment and fee note.

Body
slug, name, website, optional minPayout, feeNote, translations
Antwortet
the provider (Status 201)

PATCH/v1/admin/providers/:idInhaber

Changes a provider.

Pfad
:id provider id (UUID)
Body
any subset of the fields
Antwortet
the provider

POST/v1/admin/projectsInhaber

Creates a project. impactType is required, so no card is ever ambiguous about what the money does.

Body
slug, providerId, categoryId, title, kind, impactType, optional unitName, co2e, translations
Antwortet
the project (Status 201)

PATCH/v1/admin/projects/:idInhaber

Changes a project. kind is fixed because redemptions depend on it.

Pfad
:id project id (UUID)
Body
any subset of the fields; co2e: null removes the CO₂e figure
Antwortet
the project

POST/v1/admin/projects/:id/pricesInhaber

Adds a price. Prices are append-only and queued redemptions keep the price they were made at.

Pfad
:id project id (UUID)
Body
price, currency, fxToEur, public reason
Antwortet
the new price version (Status 201)

GET/v1/admin/queueInhaber

The pools waiting for a payout and whether they have reached the provider's minimum.

Antwortet
{ pools: [...] }

GET/v1/admin/budget/forecastInhaber

What paying out today would cost, how much the owner would have to add, and how long the budget lasts at the pace of the last 30 days.

Antwortet
balances, pools and the runway

POST/v1/admin/payoutsInhaber

Records a real payment with its receipt and turns the settled redemptions into a Deed. Payouts cannot be changed afterwards.

Body
projectId, redemptionIds, amountPaid, currency, paidAt, optional comment, and the receipt file
Gesendet als
multipart/form-data, at most 12 MB
Antwortet
the payout with its snapshot and receipt hash (Status 201)

POST/v1/admin/payouts/:id/reviewsInhaber

Records what the provider delivered after the payment. Append-only: a correction never hides the earlier entry.

Pfad
:id payout id (UUID)
Body
status (confirmed, needs_clarification, corrected), note, evidenceUrl (required for confirmed)
Antwortet
the review entry (Status 201)

Betrieb

Was ein Container-Orchestrator fragt, bevor er Verkehr schickt.

GET/healthOffen

Checks the process and its database. Answers 503 when the database is unreachable, so a broken instance is taken out of rotation.

Antwortet
{ status, database }

POST/v1/ops/trafficIntern

The website reports how many compressed bytes and responses it sent per UTC day since its last report; the API adds them up. Only totals: no address, cookie or page, so no visit can be told apart.

Body
{ days: [{ day, bytes, responses }] }; days older than 30 days or in the future are skipped
Antwortet
{ stored }, the number of days added