Humanos 1.0.5

dotnet add package Humanos --version 1.0.5
                    
NuGet\Install-Package Humanos -Version 1.0.5
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Humanos" Version="1.0.5" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Humanos" Version="1.0.5" />
                    
Directory.Packages.props
<PackageReference Include="Humanos" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Humanos --version 1.0.5
                    
#r "nuget: Humanos, 1.0.5"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Humanos@1.0.5
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Humanos&version=1.0.5
                    
Install as a Cake Addin
#tool nuget:?package=Humanos&version=1.0.5
                    
Install as a Cake Tool

Humanos SDK for C# / .NET

Official C# SDK for the Humanos API. Auto-signs every request, verifies and decrypts webhooks, and ships full type definitions.

NuGet version License: MIT

Concepts

Humanos gives your agent a cryptographic permission slip — issued by the user, scoped to a specific action, and verified at runtime.

  • Action — a reusable policy template you publish through the dashboard. Declares UserParams (values the human approves), ExecutionParams (what the agent must supply at execution time), and the rules that compare them.
  • Mandate — a signed W3C credential (type POLICY) the user issues to your agent. References one action plus the UserParams values that bind this specific approval.
  • Verifiable Presentation (VP) — a short-lived, signed proof derived from a mandate. The agent presents one each time it wants to act.
  • Verify — Humanos checks the VP's signatures, expiry, revocation status, and the action's rules against the agent's ExecutionParams. Returns allow or deny.
  • Revoke — kills a mandate. Any further VP issuance or Verify is denied.

Lifecycle

A mandate's lifetime breaks into two flows: request and approval (one-time, gets you a mandate ID) and issuance and verification (repeats every time the agent acts).

Flow 1 — Request and approval

 ┌──────────┐    ┌──────────────┐    ┌──────────┐    ┌──────────┐         ┌─ accepts ─▶  mandate issued
 │  Action  │ ─▶ │ Agent needs  │ ─▶ │  Agent   │ ─▶ │   User   │ ─▶ ─────┤
 │ defined  │    │  permission  │    │ requests │    │ decides  │         └─ rejects ─▶  flow ends
 └──────────┘    └──────────────┘    └──────────┘    └──────────┘
   step 1                              step 2          step 3

Flow 2 — Issuance and verification

 ┌──────────┐    ┌──────────────────────────────┐         ┌─ true  ─▶  allow (agent acts)
 │  Agent   │ ─▶ │ Humanos verifies VP against  │ ─▶ ─────┤
 │issues VP │    │       ExecutionParams        │         └─ false ─▶  deny (blocked)
 └──────────┘    └──────────────────────────────┘
   step 4                       step 5

Installation

dotnet add package Humanos

Targets .netstandard2.0 — works with .NET Framework 4.6.1+ and .NET 6 / 7 / 8.

Configuration

Sign up at humanos.id, then grab two sets of credentials from the dashboard:

  1. API credentialsSettings → API Keys. Copy the API Key and Signature Secret.
  2. Webhook credentialsSettings → Webhooks. Copy the Webhook Signature Secret, Webhook Encryption Secret, and Webhook Encryption Salt.

Add them to your environment (or appsettings.json, or whatever you use):

HUMANOS_API_KEY=<your-api-key>
HUMANOS_SIGNATURE_SECRET=<your-signature-secret>

HUMANOS_WEBHOOK_SIGNATURE_SECRET=<your-webhook-signature-secret>
HUMANOS_WEBHOOK_ENCRYPTION_SECRET=<your-webhook-encryption-secret>
HUMANOS_WEBHOOK_ENCRYPTION_SALT=<your-webhook-encryption-salt>

Everything else (action IDs, mandate IDs, etc.) is application data — keep it in code or your database, not in environment variables.

Quick start

using Humanos;

var client = new HumanosClient(new HumanosClientConfig
{
    BasePath = "https://api.humanos.id",
    ApiKey = Environment.GetEnvironmentVariable("HUMANOS_API_KEY")!,
    SignatureSecret = Environment.GetEnvironmentVariable("HUMANOS_SIGNATURE_SECRET")!,
});

// Sanity check — confirms the API key, signature secret, and signing handshake.
var requests = await client.Requests.ListAsync();
Console.WriteLine(requests);

A 200 OK with a (possibly empty) list means you're wired up. The walkthrough below exercises the rest of the flow.

Integration walkthrough

Six steps, matching the lifecycle diagram above. Run through them once with a test action to get an end-to-end feel.

1. Define an action

In the dashboard, create an action. An action has three parts:

  • ExecutionParams — the values the agent supplies at VerifyAsync() time.
  • UserParams — the constants the user pins at decision time.
  • Rules — deterministic CEL expressions comparing the two above.

Example:

Part Field Type
ExecutionParams amount number
category string
UserParams maxAmount number
allowedCategories array<string>

Rules then express things like executionParams.amount <= userParams.maxAmount and executionParams.category in userParams.allowedCategories.

Once defined, publish the action and copy its ID. Hold the ID as a constant in your code or store it alongside the rule it represents.

2. Issue a mandate request

Ask Humanos to issue a mandate to a user. The request bundles the contact, the action ID, and the UserParams values the user is being asked to approve.

using Humanos.Generated.Model;

var actionId = "urn:via:action:<uuid>"; // from step 1

var request = await client.Requests.CreateAsync(new GenerateRequestDto(
    contacts: new List<string> { "user@example.com" },
    securityLevel: GenerateRequestDto.SecurityLevelEnum.CONTACT,
    credentials: new List<CredentialDto>
    {
        new CredentialDto(
            scope: "agent.execute",
            type: CredentialDto.TypeEnum.POLICY,
            name: "Action mandate", // required, shown to the user
            action: new ActionRefDto(
                actionUrn: actionId,
                userParams: new Dictionary<string, object>
                {
                    ["maxAmount"] = 10000,
                    ["allowedCategories"] = new[] { "BOOKS", "OFFICE" },
                }
            )
        ),
    }
));

Console.WriteLine($"Request ID: {request.Id}");

Persist request.Id if you want to track pending approvals. The mandate ID itself becomes available once the user approves (next step).

3. User approval and mandate issuance

Humanos handles the user-facing approval flow. The user reviews the UserParams from step 2 and either approves or rejects.

Identity verification. The security code (OTP) is always delivered by email or SMS — that's how Humanos proves the user is reachable at the contact you supplied.

Where the approval UI shows up. Two options:

  • Hosted (default) — the email or SMS message contains a link to the Humanos-hosted approval page. The user clicks through, reviews, decides.
  • Embedded iframe — your application embeds the Humanos approval UI directly. The user never leaves your app. See the iframe integration guide for setup.

If KYC is required on the action, the user completes that first. If the user rejects (or KYC fails), no mandate is issued.

On accept, Humanos issues the mandate and emits a credential webhook event. The mandate ID — urn:via:credential:… — is the durable artifact: persist it on the rule record in your database (e.g., ruleId → mandateId).

The SDK exposes Webhooks.ProcessWebhook<T>(rawBody, headers, config) which verifies the signature and decrypts the payload in one call. Wire it into your ASP.NET Core endpoint:

using System.Text.Json;
using Humanos;

var config = new WebhookConfig
{
    SignatureSecret = Environment.GetEnvironmentVariable("HUMANOS_WEBHOOK_SIGNATURE_SECRET")!,
    EncryptionSecret = Environment.GetEnvironmentVariable("HUMANOS_WEBHOOK_ENCRYPTION_SECRET")!,
    EncryptionSalt = Environment.GetEnvironmentVariable("HUMANOS_WEBHOOK_ENCRYPTION_SALT")!,
};

app.MapPost("/webhook", async (HttpRequest req) =>
{
    using var reader = new StreamReader(req.Body);
    var rawBody = await reader.ReadToEndAsync();
    var headers = req.Headers.ToDictionary(h => h.Key, h => h.Value.ToString());

    var payload = Webhooks.ProcessWebhook<JsonElement>(rawBody, headers, config);

    var eventType = payload.GetProperty("eventType").GetString();
    switch (eventType)
    {
        case "credential":
            var decision = payload.GetProperty("decision").GetProperty("action").GetString();
            if (decision != "accept")
            {
                // user rejected — mark rule unenforceable, notify operator
                break;
            }
            var mandateId = payload.GetProperty("credential").GetProperty("id").GetString();
            // persist: ruleId → mandateId
            break;

        case "identity":
            // KYC / identity verification completed
            break;

        case "otp.failed":
            // user failed OTP — possible fraud signal
            break;

        case "test":
            // "Send test event" from the dashboard
            break;
    }

    return Results.Ok();
});

Important: read the raw request body before any model binding or middleware reformats it — the signature is computed over the unmodified bytes.

The credential payload shape (raw JSON):

{
  "eventType": "credential",
  "requestId": "string",
  "internalId": "string?",
  "issuerDid": "string",
  "user": { "contact": "string", "id": "string", "internalId": "string?" },
  "credential": "<CredentialEntity — full W3C VC; .id is the mandate ID>",
  "decision": { "action": "accept | reject", "date": "ISO 8601" }
}

For local development, expose your server with ngrok:

ngrok http 5000

Set the ngrok URL (e.g. https://xxxx.ngrok-free.app/webhook) under Settings → Webhooks.

If you're using the iframe channel, the same credential payload is also delivered via window.postMessage to the parent window — useful for live UI updates without a backend round-trip.

Dev shortcut: during development you can copy the mandate ID directly from the dashboard's activity table (look for the MANDATE_ISSUE entry) instead of wiring a webhook.

4. Agent issues a VP

When the agent wants to act, your backend (or whichever component enforces the rule) asks Humanos for a fresh Verifiable Presentation bound to the mandate ID captured in step 3. VPs are short-lived and single-purpose — issue a new one for every verify.

using Humanos.Generated.Model;

var mandateId = "urn:via:credential:<id>"; // captured in step 3

var vp = await client.Credentials.IssueVPAsync(
    mandateId,
    new CreatePresentationDto(
        // targetVerifier: "did:web:your-verifier.example"  // optional, see below
    )
);

Field-by-field:

  • mandateId (first positional arg) — the mandate ID captured in step 3.
  • TargetVerifier (optional body field) — DID of the intended verifier. When provided, the VP is bound to that audience via proof.domain plus a challenge nonce.

The response is a PresentationResponseEntityPresentation and Receipt. Pass vp.Presentation to step 5.

5. Humanos verifies the VP

Hand the VP plus the agent's ExecutionParams to VerifyAsync(). Humanos checks four things: signatures, expiry, revocation status, and rule compliance. 200 OK means allow; 403 means at least one check failed (the response body names the failing rule under evaluations).

await client.Credentials.VerifyAsync(new VerifyPresentationDto(
    presentation: vp.Presentation,
    executionParams: new Dictionary<string, object>
    {
        ["amount"] = 5000,
        ["category"] = "BOOKS",
    }
));

Field-by-field:

  • Presentation — the signed VP from step 4. Read it from vp.Presentation.
  • ExecutionParams — what the agent wants to do. Field names must match those declared on the action and are referenced inside rules as executionParams.<field>.

Expected outcomes for the example action:

Case ExecutionParams Result
In-bounds { ["amount"] = 5000, ["category"] = "BOOKS" } allow (200) + signed receipt
Out-of-bounds { ["amount"] = 50000, ["category"] = "FLIGHTS" } deny (403) — body names the failing rule(s)

6. Revoke a mandate

Mandates are immutable — to retire one (rule deletion, rule update, user pulling consent), call Credentials.RevokeAsync(). Once revoked, the mandate is dead: IssueVPAsync errors with credential_revoked, and any VP still held by an agent fails VerifyAsync() with the same reason. Stale rules cannot be enforced; that's the safety property.

await client.Credentials.RevokeAsync(
    mandateId,
    revokeCredentialDto: new RevokeCredentialDto(reason: "user_initiated")
);

Field-by-field:

  • credentialId (first arg) — the mandate ID.
  • Reason — free-text label recorded on the credential and in the MANDATE_REVOKED receipt. Recommended values from the VIA protocol: user_initiated, organization_policy, system_expiry.

The response is a RevokeCredentialEntityCredentialId, Status = "REVOKED", RevokedAt, and a MANDATE_REVOKED receipt. Store it for audit if useful. Enforcement uses the revocation status itself, not the receipt.

Humanos audit trail

For compliance, Humanos persists every consequential event in a mandate's lifecycle as an immutable, cryptographically signed record. You don't need to log these yourself — they're available for query at any time via client.Activity.ListAsync().

Event Trigger Captured
Mandate issued User accepts a request Action ID, UserParams, signed credential, timestamp
Mandate revoked Credentials.RevokeAsync() succeeds Mandate ID, reason, timestamp
Mandate canceled Request canceled before approval Request ID, canceler, timestamp
Human decision: accept User approves a request User, request, OTP channel (email or SMS), UI surface (hosted / iframe)
Human decision: reject User rejects a request User, request, OTP channel, UI surface
VP issue Credentials.IssueVPAsync() succeeds Mandate ID, VP, target verifier, receipt
VP issue denied Credentials.IssueVPAsync() rejected Mandate ID, reason code (e.g., credential_revoked)
Verify accept Credentials.VerifyAsync() returns 200 VP, ExecutionParams, rule evaluations, signed receipt
Verify deny Credentials.VerifyAsync() returns 403 VP, ExecutionParams, failing rule(s), signed receipt

Webhook details

Webhooks.ProcessWebhook<T> verifies the HMAC signature against the raw body, decrypts the AES-256-GCM payload, and deserializes into T. Switch on eventType (string) to dispatch:

Event Trigger
credential A credential request was approved or rejected. credential.id is the mandate ID.
identity An identity / KYC check completed.
otp.failed A user failed OTP entry.
test "Send test event" from the dashboard.

Operational notes

  • Send a test event from Settings → Webhooks before going live — confirms your URL is reachable and your handler decodes the payload.
  • Retries: failed deliveries are retried with backoff. Make your handler idempotent (key off requestId + eventType).
  • Order: events for unrelated requests may arrive out of order. Within a single request, credential precedes any follow-up events.

API reference

Every method below is fully typed; signatures live in the generated assemblies. All methods are async and return Task<T>.

// Requests — credential request lifecycle
client.Requests.ListAsync();                                          // paginate / filter your requests
client.Requests.CreateAsync(dto);                                     // issue a credential request to a user
client.Requests.DetailAsync(requestId);                               // full request including credentials and subjects
client.Requests.CancelAsync(requestId);                               // cancel a pending request
client.Requests.ResendOtpAsync(requestId);                            // resend OTP via the original channel

// Credentials — mandates and verification
client.Credentials.DetailAsync(credentialId);                         // fetch a credential by ID
client.Credentials.EvidenceAsync(evidenceId);                         // download attached evidence
client.Credentials.IssueVPAsync(vcId, createPresentationDto);         // issue a fresh VP from a mandate
client.Credentials.VerifyAsync(verifyPresentationDto);                // evaluate a VP at runtime
client.Credentials.RevokeAsync(credentialId, revokeCredentialDto: ...); // permanently revoke a mandate

// Actions — published policy templates
client.Actions.ListAsync();
client.Actions.VersionsAsync(actionId);

// Activity — audit log
client.Activity.ListAsync();

// Approval workflows (forms, consents, documents)
client.Approval.ListAsync();
client.Approval.WorkflowsAsync();

// Users
client.Users.CreateAsync(createSubjectDtoList);
client.Users.DetailAsync(contact: ...);

// DID resolution
client.Did.ResolveAsync(did);

API versioning

Humanos uses date-based API versions (e.g., 2026-03-24). The SDK pins each release to a default version and sends it as an api-version header on every request — your integration stays isolated from API changes, and you upgrade by bumping the SDK on your own schedule.

The current default is exposed via SdkInfo:

Console.WriteLine(SdkInfo.ApiVersion); // "2026-03-24"

Every method also accepts an optional aPIVersion parameter for per-call overrides — useful when testing a new version against a single endpoint before bumping globally:

await client.Credentials.VerifyAsync(
    new VerifyPresentationDto(presentation: vp.Presentation, executionParams: execParams),
    aPIVersion: "2025-12-01"  // api version for this call only
);

Action versions are independent of API versions. When you republish an action in the dashboard, mandates already issued against the older version keep referencing that version — they don't break. List published versions with client.Actions.VersionsAsync(actionId).

Error handling

The SDK throws ApiException (from Humanos.Generated.Client) on non-2xx responses. The exception carries the HTTP status and response body:

using Humanos.Generated.Client;

try
{
    await client.Requests.CreateAsync(dto);
}
catch (ApiException ex)
{
    Console.Error.WriteLine($"Status: {ex.ErrorCode}");
    Console.Error.WriteLine($"Body: {ex.ErrorContent}");
}

Troubleshooting

401 on every request. Signature mismatch. Double-check the signature secret matches the dashboard exactly — no trailing whitespace or stray newline.

400 on CreateAsync() with "name is required". Each CredentialDto needs a Name field. The example in step 2 uses "Action mandate".

Webhook never fires. Confirm the URL in Settings → Webhooks matches your live endpoint. Send a test event from the dashboard. For local dev, the ngrok tunnel must be open when the user approves.

Webhook fires but ProcessWebhook errors with "Invalid webhook signature". Make sure you're passing the raw request body (read it with a StreamReader before any model binding or middleware processes it). The signature is computed over the exact bytes Humanos sent.

VerifyAsync() returns 403 with no obvious failing rule. Check that the ExecutionParams field names exactly match the action's declared fields. A typo silently fails rule evaluation.

IssueVPAsync() returns 400 after revoke. Expected — once a mandate is revoked, VP issuance is blocked at source with credential_revoked. Existing VPs also fail VerifyAsync() for the same reason.

Support

  • Email: tech@humanos.tech

License

MIT License - see LICENSE file for details.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.5 92 5/20/2026
1.0.4 91 5/20/2026
1.0.3 92 5/18/2026
1.0.2 111 4/14/2026
1.0.1 112 4/1/2026