PolyhydraGames.API.Kick
2.5.0.70
dotnet add package PolyhydraGames.API.Kick --version 2.5.0.70
NuGet\Install-Package PolyhydraGames.API.Kick -Version 2.5.0.70
<PackageReference Include="PolyhydraGames.API.Kick" Version="2.5.0.70" />
<PackageVersion Include="PolyhydraGames.API.Kick" Version="2.5.0.70" />
<PackageReference Include="PolyhydraGames.API.Kick" />
paket add PolyhydraGames.API.Kick --version 2.5.0.70
#r "nuget: PolyhydraGames.API.Kick, 2.5.0.70"
#:package PolyhydraGames.API.Kick@2.5.0.70
#addin nuget:?package=PolyhydraGames.API.Kick&version=2.5.0.70
#tool nuget:?package=PolyhydraGames.API.Kick&version=2.5.0.70
PolyhydraGames.API.Kick
Kick integration library for Polyhydra Games hosts and ChannelCheevos.
Documentation
- Docs Index
- End-to-End Smoke Path
- Steward Docs Index
- Feature Index
- Roadmap Index
- POC
- Capability host boundary
- Reusable host contracts and compatibility fixtures
Package Facts
PackageId:PolyhydraGames.API.KickTargetFramework:net10.0Language: C#Test project:test/PolyhydraGames.API.Kick.Tests
Publishing
The .github/workflows/publish-packages.yml workflow runs on pushes to main and manual workflow_dispatch runs on main; pull requests never publish. The package job runs on api-ci-kick, restores, builds, tests, and packs exactly one nupkg into publish-out/, then hands it to downstream jobs through actions/cache. The independent publish-internal mirror job and OIDC-enabled publish-nuget-org job run on the separate nuget-api-kick-publish runner role. That publish-only runner never executes build, test, or pull-request code, so compromised code on the api-ci-kick build runner cannot persist onto the host that receives the nuget.org OIDC token. Both publish jobs first clear the retained workspace, install the same pinned .NET SDK inside their own job, and restore only the run-scoped package cache; setup-dotnet uses the runner tool cache for mirrors and a fresh per-job temp directory for nuget.org publishing. The nuget.org version guard runs with isolated python3 -I. The publish-nuget-org job uses NuGet Trusted Publishing through GitHub OIDC and has no checkout or build steps. GitHub Packages and BaGet publish from the independent, non-OIDC mirror job, which cannot delay the nuget.org job and remains non-blocking. Pull-request validation uses hosted ubuntu-latest. The api-ci-kick and nuget-api-kick-publish runner roles are provisioned by lancer1977/homelab-gitops#1028 and #1029.
The package version is <VersionPrefix>.<github.run_number>. Before publishing, the nuget.org job checks that the version is greater than the highest version listed by nuget.org. Queued default-branch runs may coalesce to the newest pending run, so the newest green commit of a burst is published.
Operator setup: create a nuget.org Trusted Publishing policy for owner lancer1977, repository lancer1977/API-Kick, and workflow file publish-packages.yml.
Standalone restore uses the public packages on nuget.org and requires no package-read token:
dotnet restore API-Kick.sln
Registration
services.AddKickApi(configuration);
services.AddKickStreaming(configuration);
The optional Kick fact mapper can be registered independently with AddKickFactMapper();
it does not require streaming services. The optional neutral provider event mapper can also be
registered independently with AddKickProviderEventMapper(); it maps webhook events to the
public PolyhydraGames.Streaming.Events contract. Gifted channel subscriptions map to gift,
while Kicks gifted map to cheer (Kick's bits-equivalent). See the capability host boundary
for the library/runtime responsibility split and event kind mapping.
Use AddKickApi for channel, livestream, OAuth, webhook, and health-check services only. Use AddKickStreaming when the host also needs chat listener, inbound source, outbound sink, and hosted streaming services; it calls AddKickApi internally, so callers do not need to register both.
Explicit-token event subscriptions
IKickEventSubscriptionsClient is a stateless client for hosts that already own
account authority and token persistence. Register it without starting streaming
workers or configuring a process-wide access token:
services.AddKickEventSubscriptions(new Uri("https://api.kick.com"))
.ConfigureHttpClient(client => client.Timeout = TimeSpan.FromSeconds(10));
var result = await subscriptions.SubscribeAsync(ownerAccessToken, broadcasterUserId,
[new KickEventSubscriptionRequest("chat.message.sent", 1)], cancellationToken);
The registration extension is in PolyhydraGames.API.Kick.Setup, the interface in
.Interfaces, and request/result types in .Models. Inject the interface as
subscriptions; the host must establish the owner's authority before providing
their token. ListAsync takes the same explicit token and broadcaster ID. Each
request sets its own bearer header; no shared credential binding or configured-token
fallback occurs. Existing AddKickApi/AddKickStreaming behavior is unchanged.
Results distinguish Ok, Unauthorized, Forbidden, Rejected, and Unknown.
HTTP 200 with all events refused is Rejected; mixed acceptance/refusal is Unknown
and requires reconciliation. Malformed/missing create acknowledgement,
transport failure, server error, or client timeout is unknown; a mutation may have
executed. Caller cancellation propagates and also does not establish non-execution.
There are no automatic retries. Reconcile with a list query before repeating an
unconfirmed create. An empty list is authoritative only when its outcome is Ok.
The client accepts explicit event/version pairs; event selection and reconciliation
lifecycle belong to the host. See Kick's subscription documentation.
Explicit-token public chat sends
IKickPublicChatClient sends a message through Kick's official POST /public/v1/chat endpoint.
AddKickApi(configuration) registers the typed client using Kick:ApiBaseUrl; each send supplies
the caller-owned access token and broadcaster ID explicitly:
var result = await publicChat.SendAsync(ownerAccessToken, broadcasterUserId, "Hello!", cancellationToken);
The client does not retain credentials or retry. A confirmed data.is_sent response produces
Sent; provider HTTP rejection or data.is_sent: false produces Rejected; malformed,
incomplete, server-error, and otherwise ambiguous responses produce Unknown with a failure code.
When Kick includes a nonblank string data.message_id in an affirmative acknowledgement, the result
exposes it as ProviderMessageId; omitted or malformed IDs remain null.
Caller cancellation propagates, while the configured HttpClient timeout returns Unknown with
send_timeout. Unknown means the caller cannot establish whether the message was sent.
For verified chat.message.sent webhooks, KickWebhookEvent.MessageId and the outer normalized
event ID remain Kick's signed header delivery/replay ID. ChatMessageId is the separate payload
message_id; neutral chat payloads use it when present. Older manually constructed webhook DTOs
without ChatMessageId retain the legacy nested-payload fallback to MessageId, which is not
evidence that the delivery and chat identities were verified as equal.
Credential-free fixture validation (including concurrent owners and cancellation):
dotnet test test/PolyhydraGames.API.Kick.Tests/PolyhydraGames.API.Kick.Tests.csproj --filter FullyQualifiedName~KickEventSubscriptionsClientTests
dotnet test test/PolyhydraGames.API.Kick.Tests/PolyhydraGames.API.Kick.Tests.csproj --filter 'FullyQualifiedName!~.Integration.'
Credential ownership and 2.0 migration
Version 2.0 removes mutable factory binding. Supply each owner's credentials for each operation;
the library never stores a process-wide “currently bound” owner. Register an
IKickCredentialProvider that resolves your host user ID to that user's Kick credentials:
var credentials = new KickCredentials(streamerOAuthToken, RefreshToken: streamerRefreshToken,
ChannelSlug: streamerKickSlug, ChannelId: streamerKickId) { OwnerId = "stable-host-user-id" };
var client = kickApiClientFactory.CreateClient(credentials);
await chatSender.SendMessageAsync(credentials, streamerKickSlug, "Hello!");
Set OwnerId to a stable identity when callers rotate tokens. A successful send retires blocked
state for earlier tokens associated with that owner. Without an OwnerId, a blocked token's
snapshot remains until it ages out.
KickOutboundSink asks the provider for credentials using the message channel's owner ID and
checks the target channel against the credential's configured Kick channel. Missing credentials
and owner/channel mismatches fail explicitly. Hosts must register their provider after
AddKickApi/AddKickStreaming to replace the default provider, which fails outbound sends.
The configured Kick:AccessToken remains available for single-account webhook auto-subscription.
Owner-bound chat sends always use the credentials passed for that operation. InitializeAsync and
BoundUserId were removed; migrate 1.x consumers to per-call credentials and an owner credential
provider before upgrading. See CHANGELOG.
Configuration
{
"Kick": {
"Enabled": true,
"BaseUrl": "https://kick.com",
"ApiBaseUrl": "https://api.kick.com",
"ClientId": "",
"ClientSecret": "",
"AccessToken": "",
"RefreshToken": "",
"ApplicationName": "PolyhydraGames",
"Auth": {
"RedirectUri": "https://localhost:5001/auth/kick/callback"
},
"Webhook": {
"Enabled": true,
"CallbackPath": "/api/kick/webhook",
"PublicUrl": "https://example.ngrok-free.app/api/kick/webhook",
"PublicKey": "",
"AutoSubscribe": false,
"EventTypes": "chat.message.sent,channel.followed,channel.subscription.new,channel.subscription.renewal,channel.reward.redemption.updated"
},
"Chat": {
"Enabled": true,
"OwnerUserId": "your-polyhydra-user-id",
"ChannelSlug": "your-kick-channel",
"ChannelId": null,
"AutoDiscoverActiveStream": true,
"EnableOutboundChat": true,
"ReconnectDelay": "00:00:05",
"MaxReconnectDelay": "00:01:00"
}
}
}
Configuration Reference
| Key | Type | Description |
|---|---|---|
Enabled |
bool |
Enable/disable entire Kick integration |
BaseUrl |
string |
Kick website base URL |
ApiBaseUrl |
string |
Kick API base URL |
ClientId |
string |
OAuth client ID. Required for channel and livestream lookups (app access token) |
ClientSecret |
string |
OAuth client secret. Required for channel and livestream lookups (app access token) |
AccessToken |
string |
OAuth access token used by explicit single-account webhook auto-subscription |
RefreshToken |
string |
OAuth refresh token |
ApplicationName |
string |
User-Agent application name |
Auth:RedirectUri |
string |
OAuth redirect URI for code exchange |
Webhook:Enabled |
bool |
Enable webhook parsing and validation |
Webhook:CallbackPath |
string |
Local webhook callback route |
Webhook:PublicUrl |
string |
Public webhook URL for Kick subscription config |
Webhook:PublicKey |
string |
Optional webhook signature public key |
Webhook:AutoSubscribe |
bool |
Auto-subscribe to configured webhook events |
Webhook:EventTypes |
string |
Comma-separated webhook event types |
Chat:Enabled |
bool |
Enable/disable chat listener |
Chat:OwnerUserId |
string |
Platform owner user ID for message routing |
Chat:ChannelSlug |
string |
Kick channel slug to monitor |
Chat:ChannelId |
string |
Alternative channel identifier |
Chat:AutoDiscoverActiveStream |
bool |
Add active streamId metadata when available |
Chat:EnableOutboundChat |
bool |
Enable outbound chat messaging |
Chat:ReconnectDelay |
timespan |
Initial reconnect delay |
Chat:MaxReconnectDelay |
timespan |
Maximum reconnect delay |
Notes
- Keep the docs aligned with the actual Kick and chat integration surface.
- Prefer concrete configuration and integration notes over implied behavior.
- Webhook ingestion uses KickLib's signature validation and webhook event parser.
- Source and test projects consume their shared Polyhydra dependencies as NuGet packages, so the restore/test path works from a standalone clone. An empty-cache restore resolves these published dependencies from nuget.org without a package-read token.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- KickLib (>= 1.14.1)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.14)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.14)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.14)
- Microsoft.Extensions.Http (>= 10.0.14)
- PolyhydraGames.Auth.Abstractions (>= 1.2.0)
- PolyhydraGames.Core.Global (>= 1.0.4.27)
- PolyhydraGames.Core.Interfaces (>= 3.0.0.26)
- PolyhydraGames.Extensions (>= 2.1.2.5)
- PolyhydraGames.Platforms.Abstractions (>= 1.2.0)
- PolyhydraGames.PostOffice.Abstractions (>= 1.2.0)
- PolyhydraGames.PostOffice.Core (>= 1.2.0)
- PolyhydraGames.Streaming.Events (>= 0.1.1.5)
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.5.0.70 | 784 | 11/11/2026 |
| 2.4.0.69 | 108 | 10/31/2026 |
| 2.4.0.68 | 8,474 | 10/15/2026 |
| 2.4.0.67 | 101 | 10/15/2026 |
| 2.4.0.66 | 1,226 | 10/12/2026 |
| 2.3.0.65 | 104 | 10/12/2026 |
| 2.3.0.64 | 102 | 10/11/2026 |
| 2.2.1.63 | 3,802 | 10/10/2026 |
| 2.2.0.62 | 2,123 | 10/8/2026 |
| 2.1.1.61 | 108 | 10/8/2026 |
| 2.1.1.60 | 149 | 10/6/2026 |
| 2.1.0.59 | 107 | 10/6/2026 |
| 2.0.8.58 | 103 | 10/6/2026 |
| 2.0.8.56 | 110 | 10/6/2026 |
| 2.0.8.54 | 835 | 10/6/2026 |
| 2.0.7.53 | 123 | 10/5/2026 |
| 2.0.6.52 | 102 | 10/5/2026 |
| 2.0.5.51 | 107 | 10/5/2026 |
| 2.0.4.50 | 108 | 10/5/2026 |
| 2.0.3.49 | 246 | 10/5/2026 |