Product update

Coupons Get An Off Switch

TL;DR

deactivate_coupon and reactivate_coupon are live. Ending a promo is now one sentence to your AI, same as starting one. Deactivating is not deleting: the code stops working at checkout immediately, every order that used it still names it, and list_coupons keeps returning it with active: false. Both tools need an admin or owner key.

Making a discount code in GoCushy has always been one sentence. "Create a 20% code called SAVE20." Your AI calls create_coupon and the code is live.

Ending one was not one sentence. You opened the dashboard, found the right screen, and switched it off by hand.

For most software that asymmetry is a shrug. For a money tool it is the wrong way round. Starting a promo is something you plan; stopping one is usually something you need to do now. A code leaks onto a deal site. A partner posts it publicly instead of to their list. A launch ended on Friday and it is Sunday morning. In every one of those, the urgent action is stop — and stop was the slow one, the one that needed a laptop and a login.

deactivate_coupon and reactivate_coupon close that gap. They went out with v34 of our MCP app. That is the whole change, and it is small, but it moves the fast direction from create-only to both.

Deactivate is not delete

This distinction matters more than it sounds, so I want to be exact about it.

deactivate_coupon changes one thing: whether the code works at checkout. It stops working immediately.

Three things it does not do:

I could have shipped a delete instead. Deletes are simpler to build and much worse to live with, because the record of what a buyer paid and why is exactly the thing you need eight months later when somebody disputes a charge, or an accountant asks why an invoice reads $39.20 against a $49 offer. A code that vanishes takes the explanation with it and leaves you with an unexplained number.

So it is a switch, not a shredder.

The life of a discount code, and what deactivating leaves alone A top row of three states — created and live, deactivated, reactivated — sits above a single band representing order history. Dashed lines drop from each state into the history band. Under the deactivated state the history band gains nothing and loses nothing; the orders written before and after it remain. The code create_coupon Live active: true deactivate_coupon Off active: false reactivate_coupon Live again active: true Order history — nothing here is rewritten SAVE20 SAVE20 No new rows written None removed either SAVE20
Deactivating is a state change, not a removal. During the middle column no new order can use the code and no old one loses it — and the coupon itself is still in list_coupons the whole time, flagged active: false.

It needs an admin key, and that is deliberate

Both tools require an admin or owner API key.

That is a real limit and I would rather state it than have you discover it. If you have handed an agent a lesser key, it will not be able to switch your promo off — and it should not be able to.

Here is the reasoning. Turning a discount on or off changes what every buyer pays from that moment forward. It is not a read, and it is not a small write. An agent that can tell you which coupons exist and how they are performing is doing something categorically different from an agent that can change what your checkout charges. The key is where that line is drawn, so "my AI can end a promo" and "anything holding any of my keys can end a promo" stay two different sentences.

What the checkout actually decides

A coupon is not simply on or off. When a code arrives at checkout, the server works through a short list, and every answer has to be yes before a cent comes off.

All four of those live on the server. None of them live in the browser. The buyer's browser sends a string; what that string is worth is worked out on our side.

Four server-side checks between a typed code and a discount A dashed line divides the browser on the left from the server on the right. The browser sends only the code. On the server it passes through four stacked checks — active, offer scope, redemption cap, expiry. A dashed rail leads from any failed check to a No discount, full price outcome. Passing all four leads to the discount being applied. Browser Server — gocushy Buyer types SAVE20 Just the code Is it active? Yes Does it cover this offer? Yes Under the redemption cap? Yes Still before the expiry? Any no No discount Full price All four yes: it applies Computed on the server
Deactivating flips the first check only. The other three keep working exactly as before, which is why a scoped, capped, expiring code behaves the same whether you ever touch the switch or not.

The coupon rules worth knowing

Since I am writing about coupons anyway, here is the set of rules that decides whether the code you have in mind is possible.

A percentage or an amount, never both. A coupon takes a percentage off, or a fixed amount off. There is no code that does both at once.

Scope is optional. Leave it open and the code works across your offers; scope it to one offer and it works only there.

A redemption cap is optional. Set a number and the code stops once it has been used that many times.

An expiry is optional. Set a date and the code stops on its own.

It comes off before tax. The discount is applied server-side, and tax is worked out from what is left. Tax follows the amount actually paid, not the amount on the sticker.

It can never take a charge below $0.50. That floor is real, and it has a consequence worth thinking about before you write a big code: a coupon cannot make something free, because 100% off lands under the floor.

On a subscription, the discount applies to the first payment. Renewals go out at full price. That is a joining discount, not a permanent one, and you should price around it rather than be surprised by it. It also means deactivating a code cannot claw back an ongoing discount from an existing subscriber, because there was never an ongoing discount to claw back.

Where a coupon lands in the order of a single charge A vertical ladder of five steps for one charge: the price of forty nine dollars, then the coupon discount of nine dollars eighty applied on the server, then a floor that never lets the charge fall below fifty cents, then tax worked out on thirty nine dollars twenty, then the amount charged. One charge, in order Your price $49.00 SAVE20 comes off here, on the server −$9.80 Floor: never below $0.50 Hard limit Tax is worked out at this point On $39.20 Charged $39.20 + tax
The order is the point. Because the discount comes off before tax, tax is calculated on $39.20 rather than $49.00 — and the $0.50 floor sits between the two, which is why no code can drive a charge to zero.

Where a coupon is the wrong tool

Four cases where I would reach for something else.

And one habit worth forming: if your promo has a known end date, set the expiry when you create it. Then the ending is already handled and nobody has to remember anything on a Sunday. The off switch is for the endings you did not plan — the leak, the screenshot, the partner who posted it publicly. Those are the ones that need to be one sentence.

The general shape of this

I keep coming back to the same rule when we build for agents. Anything an agent can start, it should be able to stop, on the same terms and in the same breath. A tool that only creates is not really automated — it just moves the work to the bit where you are least likely to have a laptop open.

Coupons were the clearest example of that gap in GoCushy. Now they are not.

Read the docs →