I’m building a “replace card on file” flow for an active subscription using the .NET SDK (Square C# SDK), and the final step — updating the subscription’s card_id — consistently fails with a BAD_REQUEST error.
What I’m trying to do:
Replace a customer’s card on file, then re-point their existing subscription at the new card so future billing cycles charge the new card instead of the old one.
var body = new CreateCustomerCardRequest.Builder(model.nonce)
.BillingAddress(bodyBillingAddress)
.CardholderName(model.CardholderName)
.VerificationToken(model.squareVerificationToken)
.Build();
customersApi.CreateCustomerCard(model.SquareCustomerId, body);
Update the subscription’s card (this is the step that fails):
public UpdateSubscriptionResponse UpdateCardInExistingSubsciption(string subscriptionId, string cardId)
{
var existingSubscription = subscriptionsApi.RetrieveSubscription(subscriptionId).Subscription;
var bodySubscription = new Square.Models.Subscription.Builder()
.Id(subscriptionId)
.CardId(cardId)
.Version(existingSubscription.Version)
.Build();
var body = new UpdateSubscriptionRequest.Builder()
.Subscription(bodySubscription)
.Build();
return subscriptionsApi.UpdateSubscription(subscriptionId, body);
}
After this Error received.
Error Message: “Response Not OK”
UpdateSubscription is a sparse update and only changes the fields you send and leaves everything else at its current server value. So I think your request body should contain just card_id and version, nothing else. Drop Id from the builder; it’s a read-only field and the subscription ID already goes in the path parameter.
The error itself: “state that cannot be updated” is about the subscription’s status, so retrieve it and check. Valid values are PENDING, ACTIVE, PAUSED, CANCELED, DEACTIVATED, and COMPLETED.
If it’s DEACTIVATED, you can recover it with ResumeSubscription. Worth knowing: deactivation isn’t caused by card declines — it’s things like a missing or invalid customer email, a deleted customer profile, or a deactivated location. Call ListSubscriptionEvents and look at the DEACTIVATE_SUBSCRIPTION event’s info object for the exact reason code. Fix that first, since the resume call fails unless the underlying issue is resolved. Also note that resuming mid-cycle triggers a prorated invoice.
If it’s CANCELED, there’s no documented way to resume it — you’d create a new subscription.
If it’s ACTIVE but has a canceled_date, that’s just a scheduled future cancellation, not a terminal state. You can clear it by setting that field to null.
Two other things. “Response Not OK” is hiding the real message — catch ApiException and log the Errors collection to get the category, code, detail, and field. That will tell you exactly what Square is rejecting.
And CreateCustomerCard/DeleteCustomerCard are deprecated in favour of the Cards API (CreateCard/DisableCard); the card ID it returns works fine as card_id. Either way, add the new card and update the subscription before removing the old one, so the subscription is never pointing at a card that no longer exists.