.NET libraries for logistics carrier APIs. Currently supports DHL (Shipping, Tracking, Pickup, Returns, Internetmarke, Location Finder), GO! Express (Shipping, Tracking), and LetterXpress (Letters / SMART@MAIL) — all targeting .NET 8, 9, and 10.
Unofficial project. Parcel.NET is an independent community library. It is not affiliated with, endorsed by, or supported by DHL Group, GO! Express & Logistics, or A&O Fischer / LetterXpress. See Trademarks and disclaimer.
Feature highlights:
- Carrier-agnostic abstractions (
IShipmentService,ITrackingService) for multi-carrier support - DHL Parcel DE Shipping API v2 — create, validate, cancel shipments, manifests
- DHL Parcel DE Tracking (XML v0) and Unified Tracking (JSON) with signature retrieval
- DHL Pickup API v3 — create, cancel, and query pickup orders
- DHL Returns API v1 — create return orders, list return locations
- DHL Internetmarke/Portokasse API v1 — stamps, catalog, wallet, cart checkout
- DHL Unified Location Finder v1 — search by address, geo-coordinates, or keyword
- GO! Express Shipping — create, cancel shipments, label generation
- GO! Express Tracking — track shipments
- LetterXpress API v3 — print jobs, SMART@MAIL e-mail jobs, balance, price, transactions, invoices
- Builder-pattern DI registration with typed
HttpClientpipeline - OAuth ROPC, API key, and Basic Auth — handled automatically per carrier
- Sandbox / test mode support for DHL, GO! Express, and LetterXpress
- .NET 8.0, 9.0, or 10.0
- A DHL developer account, GO! Express credentials, and/or a LetterXpress account
# Individual packages
dotnet add package Parcel.NET.Dhl.Shipping
dotnet add package Parcel.NET.Dhl.Tracking
dotnet add package Parcel.NET.GoExpress.Shipping
dotnet add package Parcel.NET.LetterXpress.Letters
# Or install everything for a carrier
dotnet add package Parcel.NET.Dhl.All
dotnet add package Parcel.NET.GoExpress.All
dotnet add package Parcel.NET.LetterXpress.All
# Or install all carriers at once
dotnet add package Parcel.NET.Allbuilder.Services.AddDhl(options =>
{
options.ApiKey = "your-api-key";
options.ApiSecret = "your-api-secret";
options.Username = "your-username";
options.Password = "your-password";
options.UseSandbox = true;
})
.AddDhlShipping()
.AddDhlTracking()
.AddDhlUnifiedTracking()
.AddDhlPickup()
.AddDhlReturns()
.AddDhlInternetmarke()
.AddDhlLocationFinder();builder.Services.AddGoExpress(options =>
{
options.Username = "your-username";
options.Password = "your-password";
options.CustomerId = "1234567";
options.ResponsibleStation = "FRA";
options.UseSandbox = true;
})
.AddGoExpressShipping()
.AddGoExpressTracking();builder.Services.AddLetterXpress(options =>
{
options.Username = "your-username";
options.ApiKey = "your-api-key";
options.UseTestMode = true; // jobs go to the Postbox instead of being processed
})
.AddLetterXpressLetters();Send a letter as a print job:
var letterXpress = serviceProvider.GetRequiredService<ILetterXpressClient>();
var balance = await letterXpress.GetBalanceAsync();
var job = await letterXpress.CreatePrintJobAsync(new LetterRequest
{
File = await File.ReadAllBytesAsync("invoice.pdf"),
Specification = new LetterSpecification
{
Color = LetterColor.BlackWhite,
Mode = PrintMode.Simplex,
Shipping = ShippingType.National
}
});Note: The LetterXpress API is rate-limited to 120 requests/minute and accepts at most 50 MB per request. The client validates the 50 MB limit locally; throttling/retry on HTTP 429 is left to the caller's
HttpClientpipeline (e.g. aMicrosoft.Extensions.Http.Resiliencehandler).
Use the carrier-agnostic IShipmentService or the carrier-specific client:
var shippingService = serviceProvider.GetRequiredService<IShipmentService>();
var request = new DhlShipmentRequest
{
BillingNumber = "33333333330101",
Product = DhlProduct.V01PAK,
Shipper = new Address
{
Name = "Max Mustermann",
Street = "Musterstraße",
HouseNumber = "1",
PostalCode = "53113",
City = "Bonn",
CountryCode = "DEU"
},
Consignee = new Address
{
Name = "Erika Musterfrau",
Street = "Berliner Straße",
HouseNumber = "42",
PostalCode = "10117",
City = "Berlin",
CountryCode = "DEU"
},
Packages = [new Package { Weight = 2.5 }]
};
var response = await shippingService.CreateShipmentAsync(request);
Console.WriteLine($"Shipment: {response.ShipmentNumber}");var trackingService = serviceProvider.GetRequiredService<ITrackingService>();
var result = await trackingService.TrackAsync("00340434161094042557");
Console.WriteLine($"Status: {result.Status}");
foreach (var evt in result.Events)
{
Console.WriteLine($" {evt.Timestamp}: {evt.Description} ({evt.Location})");
}IDhlShippingClient extends the carrier-agnostic IShipmentService with DHL-specific operations:
var dhlClient = serviceProvider.GetRequiredService<IDhlShippingClient>();
// Validate before creating
var validation = await dhlClient.ValidateShipmentAsync(request);
if (validation.Valid)
{
var response = await dhlClient.CreateShipmentAsync(request);
}
// Create daily closing manifest
var manifest = await dhlClient.CreateManifestAsync();Two tracking clients are available:
| Client | API | Format | Sandbox |
|---|---|---|---|
IDhlTrackingClient |
Parcel DE Tracking v0 | XML | Yes |
IDhlUnifiedTrackingClient |
Unified Tracking | JSON | No |
// Parcel DE Tracking with signature retrieval
var trackingClient = serviceProvider.GetRequiredService<IDhlTrackingClient>();
var result = await trackingClient.TrackAsync("00340434161094042557");
var signature = await trackingClient.GetSignatureAsync("00340434161094042557");
// Unified Tracking (JSON)
var unifiedClient = serviceProvider.GetRequiredService<IDhlUnifiedTrackingClient>();
var result = await unifiedClient.TrackAsync("00340434161094042557");var pickupClient = serviceProvider.GetRequiredService<IDhlPickupClient>();
var order = await pickupClient.CreatePickupOrderAsync(new PickupOrderRequest { /* ... */ });
var details = await pickupClient.GetPickupOrderAsync(order.OrderNumber);
await pickupClient.CancelPickupOrderAsync(order.OrderNumber);var returnsClient = serviceProvider.GetRequiredService<IDhlReturnsClient>();
var returnOrder = await returnsClient.CreateReturnOrderAsync(new ReturnOrderRequest { /* ... */ });
var locations = await returnsClient.GetReturnLocationsAsync("DEU");var internetmarkeClient = serviceProvider.GetRequiredService<IDhlInternetmarkeClient>();
var userInfo = await internetmarkeClient.GetUserInfoAsync();
var catalog = await internetmarkeClient.GetCatalogAsync();
var balance = await internetmarkeClient.GetWalletBalanceAsync();
var cart = await internetmarkeClient.InitializeCartAsync(new CartRequest { /* ... */ });
var checkout = await internetmarkeClient.CheckoutCartAsync(cart.CartId);var locationFinder = serviceProvider.GetRequiredService<IDhlLocationFinderClient>();
var byAddress = await locationFinder.FindByAddressAsync("DEU", "Bonn", "53113");
var byGeo = await locationFinder.FindByGeoAsync(50.7374, 7.0982, 5000);
var location = await locationFinder.GetLocationByIdAsync("8003-4012221");var goClient = serviceProvider.GetRequiredService<IGoExpressShippingClient>();
var response = await goClient.CreateShipmentAsync(request);
var label = await goClient.GenerateLabelAsync(response.ShipmentNumber, GoExpressLabelFormat.Pdf);
await goClient.CancelShipmentAsync(response.ShipmentNumber);var trackingService = serviceProvider.GetRequiredService<ITrackingService>();
var result = await trackingService.TrackAsync("GO123456789");| Property | Description | Required |
|---|---|---|
ApiKey |
DHL API key (OAuth client_id) | Yes |
ApiSecret |
DHL API secret (OAuth client_secret) | For Shipping |
Username |
DHL ROPC username | For Shipping |
Password |
DHL ROPC password | For Shipping |
TrackingUsername |
Parcel DE Tracking credential | For Tracking (XML) |
TrackingPassword |
Parcel DE Tracking credential | For Tracking (XML) |
InternetmarkeUsername |
Internetmarke credential | For Internetmarke |
InternetmarkePassword |
Internetmarke credential | For Internetmarke |
UseSandbox |
Use sandbox environment | No |
| Property | Description | Required |
|---|---|---|
Username |
Basic Auth username | Yes |
Password |
Basic Auth password | Yes |
CustomerId |
Customer ID (max 7 chars) | Yes |
ResponsibleStation |
Station code (3 chars) | For Shipping |
UseSandbox |
Use sandbox environment | No |
Parcel.NET.Abstractions (IShipmentService, ITrackingService, models)
|
+-- Parcel.NET.Dhl (DhlOptions, DhlTokenService, auth handlers)
| +-- Dhl.Shipping (IDhlShippingClient)
| +-- Dhl.Tracking (IDhlTrackingClient)
| +-- Dhl.UnifiedTracking(IDhlUnifiedTrackingClient)
| +-- Dhl.Pickup (IDhlPickupClient)
| +-- Dhl.Returns (IDhlReturnsClient)
| +-- Dhl.Internetmarke (IDhlInternetmarkeClient)
| +-- Dhl.LocationFinder (IDhlLocationFinderClient)
|
+-- Parcel.NET.GoExpress (GoExpressOptions, auth handler)
+-- GoExpress.Shipping (IGoExpressShippingClient)
+-- GoExpress.Tracking (GoExpressTrackingClient)
All API errors throw typed exceptions:
try
{
var response = await shippingService.CreateShipmentAsync(request);
}
catch (ShippingException ex)
{
Console.WriteLine($"Status: {ex.StatusCode}, Error: {ex.ErrorCode}");
Console.WriteLine($"Raw response: {ex.RawResponse}");
}See CONTRIBUTING.md for build instructions, guidelines, and how to add new carriers.
Parcel.NET is an independent, community-maintained open-source project. It is not an official product of any carrier and is not affiliated with, authorized by, endorsed by, or sponsored by DHL Group, GO! Express & Logistics Deutschland GmbH, or A&O Fischer GmbH & Co. KG.
"DHL", "Deutsche Post", "Packstation", "Internetmarke", "Portokasse" and "E-POST" are trademarks or registered trademarks of Deutsche Post AG / DHL Group. "GO!" and "GO! Express & Logistics" are trademarks or registered trademarks of GO! Express & Logistics Deutschland GmbH. "LetterXpress" and "SMART@MAIL" are trademarks or registered trademarks of A&O Fischer GmbH & Co. KG. All other product names, logos and brands are the property of their respective owners. They are used in this project, in its documentation and in its package names solely to identify the third-party APIs this library communicates with; such descriptive use implies no endorsement, sponsorship or business relationship.
The library is provided under the MIT license, as-is and without warranty of any kind. It carries no support agreement or service level of any kind from the carriers, and using it does not change your obligations under each carrier's own terms of service — you still need your own contract and credentials with each carrier. Questions about the APIs themselves belong to their respective providers; questions about this library belong in its issue tracker.
If you hold rights to any of the marks named above and object to their use here, please open an issue and it will be addressed.
MIT — see LICENSE for details.
