Puntjes.Sdk 2.2.1

dotnet add package Puntjes.Sdk --version 2.2.1
                    
NuGet\Install-Package Puntjes.Sdk -Version 2.2.1
                    
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="Puntjes.Sdk" Version="2.2.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Puntjes.Sdk" Version="2.2.1" />
                    
Directory.Packages.props
<PackageReference Include="Puntjes.Sdk" />
                    
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 Puntjes.Sdk --version 2.2.1
                    
#r "nuget: Puntjes.Sdk, 2.2.1"
                    
#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 Puntjes.Sdk@2.2.1
                    
#: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=Puntjes.Sdk&version=2.2.1
                    
Install as a Cake Addin
#tool nuget:?package=Puntjes.Sdk&version=2.2.1
                    
Install as a Cake Tool

Puntjes.Sdk

The official .NET client for the Puntjes loyalty API. It runs on .NET 8 and .NET 10.

dotnet add package Puntjes.Sdk

Quick start

Create your API credentials in the Puntjes admin portal. The secret is shown once.

using Puntjes.Sdk;
using Puntjes.Sdk.Generated.Models;

var puntjes = PuntjesClientFactory.Create(
    Environment.GetEnvironmentVariable("PUNTJES_CLIENT_ID")!,
    Environment.GetEnvironmentVariable("PUNTJES_CLIENT_SECRET")!);

// A customer scans their card at the till.
var customer = (await puntjes.Customers.Lookup.GetAsync(c => c.QueryParameters.Identifier = scannedCard))?.Data;
Console.WriteLine(customer?.WalletBalance);

// Record the purchase. Amounts are in cents.
var transaction = (await puntjes.Transactions.PostAsync(new CreateTransactionData
{
    Identifier = scannedCard,
    TotalAmount = 4200,
    IdempotencyKey = $"order-{orderId}",
}))?.Data;
Console.WriteLine(transaction?.PointsEarned);

PuntjesClientFactory.Create returns a client generated from the Puntjes OpenAPI document, so it has every call the API has. A call follows the URL: puntjes.Customers["42"].Wallet.GetAsync() is GET /customers/42/wallet. Read the Data part of the answer.

examples/QuickStart is a complete program: dotnet run --project examples/QuickStart -- <card or email>.

Options

var puntjes = PuntjesClientFactory.Create(id, secret, new PuntjesOptions
{
    BaseUrl = "https://puntjes.app",  // with or without /api/v1
    MaxRetries = 2,                   // 0 turns retries off
    Timeout = TimeSpan.FromSeconds(30), // per attempt
    TokenStore = myDistributedStore,  // an ITokenStore; memory by default
});

What the SDK does for you

  • Authentication. It asks for a client_credentials token on the first call, keeps it until 30 seconds before it expires, and asks once more when the API rejects it. Parallel calls share one request for a token. An ITokenStore keeps each token under a key made from the host and the credentials, so one shared store (a Redis cache, for example) can serve the clients of many shops without mixing their tokens.
  • Retries. A GET, PUT, PATCH or DELETE, and a POST that carries an idempotency_key, is sent again after a network failure, a 5xx answer or RATE_LIMITED. It waits 0.5 s, then 1 s, or what Retry-After says. A POST without a key is never sent twice, so a purchase is never recorded twice. PLAN_LIMIT_EXCEEDED is never retried. An attempt that takes longer than Timeout counts as a network failure. A failed token request is retried the same way.
  • Idempotency keys. IdempotencyKey.New() makes one. Send the same key again to repeat a call safely.

Errors

Every error is a PuntjesException, with a subclass per kind:

Exception When
AuthenticationException 401: wrong credentials or a rejected token
ForbiddenException 403
NotFoundException 404
ConflictException 409
ValidationException 422 VALIDATION_ERROR; ErrorsFor("field") gives the messages
RateLimitException 429 RATE_LIMITED; RetryAfter says how long to wait
PlanLimitExceededException 429 PLAN_LIMIT_EXCEEDED: the shop's plan is used up
ServerException 5xx
TransportException No answer from the API, or an answer that is not from the API

A 422 for a business rule, such as INSUFFICIENT_BALANCE, is a plain PuntjesException.

using Puntjes.Sdk.Exceptions;
using Puntjes.Sdk.Generated.Errors;

try
{
    await puntjes.Redemptions.PostAsync(body);
}
catch (PuntjesException e) when (e.ErrorCode == ErrorCode.InsufficientBalance)
{
    Console.WriteLine($"Not enough points. Request {e.RequestId}");
}

ErrorCode is null for a code that is newer than your SDK; RawCode always holds the code as the API sent it.

Pages

await foreach (var product in Paginator.AllAsync<ProductData>(async (page, ct) =>
{
    var answer = await puntjes.Products.GetAsync(c => c.QueryParameters.Page = page, ct);
    return (answer?.Data?.Data, answer?.Data?.Meta);
}))
{
    Console.WriteLine(product.Name);
}

Apple wallet pass

byte[] pkpass = await WalletPass.AppleAsync(puntjes, customerId);

How this package is made

The code under src/Puntjes.Sdk/Generated is generated by Kiota from spec/api.json. Never edit it by hand: CI regenerates it and fails on any difference.

  • scripts/generate.sh regenerates it, with Kiota and the .NET SDK pinned by image digest.
  • When Puntjes publishes a new API spec, .github/workflows/regenerate.yml opens a PR with the new code. A minor or patch spec merges itself when CI is green; a major one waits for a person.
  • .github/workflows/release.yml runs release-please. Its release PR merges itself when CI is green, and the tag publishes the package to nuget.org.

nuget.org trusted publishing

The publish job has no stored API key. nuget.org trades the job's GitHub token for a key that lives one hour. The owner of the puntjesapp account on nuget.org sets this up once:

  1. On nuget.org, open Trusted Publishing and add a policy for GitHub Actions.
  2. Repository owner PuntjesApp, repository puntjes-dotnet, workflow file release.yml, no environment.
  3. In this repository, set the variable NUGET_USER to puntjesapp.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  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 is compatible.  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. 
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
2.2.1 37 10/18/2026
2.2.0 47 10/18/2026
2.1.0 72 10/15/2026
2.0.0 84 10/12/2026
1.0.0 89 10/11/2026