diff --git a/src/_zkapauthorizer/model.py b/src/_zkapauthorizer/model.py
index 10604187f7ecdae991c5a9448e1fc3612de9f1bd..fe2507b22a6b97842897bb4e60b6c36e6f20bd6c 100644
--- a/src/_zkapauthorizer/model.py
+++ b/src/_zkapauthorizer/model.py
@@ -216,7 +216,7 @@ class VoucherStore(object):
         cursor.execute(
             """
             SELECT
-                [number], [created], [state], [finished], [token-count]
+                [number], [created], [state], [finished], [token-count], [public-key]
             FROM
                 [vouchers]
             WHERE
@@ -300,7 +300,7 @@ class VoucherStore(object):
         cursor.execute(
             """
             SELECT
-                [number], [created], [state], [finished], [token-count]
+                [number], [created], [state], [finished], [token-count], [public-key]
             FROM
                 [vouchers]
             """,
@@ -339,12 +339,14 @@ class VoucherStore(object):
             SET [state] = ?
               , [token-count] = ?
               , [finished] = ?
+              , [public-key] = ?
             WHERE [number] = ?
             """,
             (
                 voucher_state,
                 len(unblinded_tokens),
                 self.now(),
+                public_key,
                 voucher,
             ),
         )
@@ -712,8 +714,20 @@ class Redeeming(object):
 
 @attr.s(frozen=True)
 class Redeemed(object):
+    """
+    The voucher was successfully redeemed.  Associated tokens were retrieved
+    and stored locally.
+
+    :ivar datetime finished: The time when the redemption finished.
+
+    :ivar int token_count: The number of tokens the voucher was redeemed for.
+
+    :ivar unicode public_key: The public part of the key used to sign the
+        tokens for this voucher.
+    """
     finished = attr.ib(validator=attr.validators.instance_of(datetime))
     token_count = attr.ib(validator=attr.validators.instance_of((int, long)))
+    public_key = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(unicode)))
 
     def should_start_redemption(self):
         return False
@@ -723,6 +737,7 @@ class Redeemed(object):
             u"name": u"redeemed",
             u"finished": self.finished.isoformat(),
             u"token-count": self.token_count,
+            u"public-key": self.public_key,
         }
 
 
@@ -819,6 +834,7 @@ class Voucher(object):
                 return Redeemed(
                     parse_datetime(row[0], delimiter=u" "),
                     row[1],
+                    row[2],
                 )
             raise ValueError("Unknown voucher state {}".format(state))
 
@@ -859,6 +875,7 @@ class Voucher(object):
             state = Redeemed(
                 finished=parse_datetime(state_json[u"finished"]),
                 token_count=state_json[u"token-count"],
+                public_key=state_json[u"public-key"],
             )
         elif state_name == u"unpaid":
             state = Unpaid(
diff --git a/src/_zkapauthorizer/schema.py b/src/_zkapauthorizer/schema.py
index cf71a9c2a9d11b26ba4756e505284770a6952dd3..502628aca377091c6820903b839930f73c2d6195 100644
--- a/src/_zkapauthorizer/schema.py
+++ b/src/_zkapauthorizer/schema.py
@@ -125,6 +125,12 @@ _UPGRADES = {
         )
         """,
     ],
+
+    1: [
+        """
+        ALTER TABLE [vouchers] ADD COLUMN [public-key] text
+        """,
+    ],
 }
 
 def _check_consistency():
diff --git a/src/_zkapauthorizer/tests/strategies.py b/src/_zkapauthorizer/tests/strategies.py
index 9157bdea6e8611ad340faa473c91296fff8b9a07..a63af545766b05cd166c36db151e5d9fe4f19b2e 100644
--- a/src/_zkapauthorizer/tests/strategies.py
+++ b/src/_zkapauthorizer/tests/strategies.py
@@ -308,6 +308,7 @@ def voucher_states():
             Redeemed,
             finished=datetimes(),
             token_count=one_of(integers(min_value=1)),
+            public_key=dummy_ristretto_keys(),
         ),
         builds(
             DoubleSpend,
diff --git a/src/_zkapauthorizer/tests/test_client_resource.py b/src/_zkapauthorizer/tests/test_client_resource.py
index f901d020fa1e23d9b26c141369996560d00a0d44..423f4950de7cadac563576da99496f0c47f18c4b 100644
--- a/src/_zkapauthorizer/tests/test_client_resource.py
+++ b/src/_zkapauthorizer/tests/test_client_resource.py
@@ -768,6 +768,7 @@ class VoucherTests(TestCase):
                 state=Equals(Redeemed(
                     finished=now,
                     token_count=NUM_TOKENS,
+                    public_key=None,
                 )),
             ),
         )
@@ -913,6 +914,7 @@ class VoucherTests(TestCase):
                         state=Redeemed(
                             finished=now,
                             token_count=NUM_TOKENS,
+                            public_key=None,
                         ),
                     ).marshal()
                     for voucher
diff --git a/src/_zkapauthorizer/tests/test_controller.py b/src/_zkapauthorizer/tests/test_controller.py
index 9333ed1d7b5c589e244ae77a9c6aa9e15a3b1acc..1c1af180ec01737e5e699da40f4575047273de27 100644
--- a/src/_zkapauthorizer/tests/test_controller.py
+++ b/src/_zkapauthorizer/tests/test_controller.py
@@ -116,6 +116,7 @@ from .strategies import (
     tahoe_configs,
     vouchers,
     voucher_objects,
+    dummy_ristretto_keys,
     clocks,
 )
 from .matchers import (
@@ -149,15 +150,15 @@ class PaymentControllerTests(TestCase):
             Equals(model_Pending()),
         )
 
-    @given(tahoe_configs(), datetimes(), vouchers())
-    def test_redeemed_after_redeeming(self, get_config, now, voucher):
+    @given(tahoe_configs(), dummy_ristretto_keys(), datetimes(), vouchers())
+    def test_redeemed_after_redeeming(self, get_config, public_key, now, voucher):
         """
         A ``Voucher`` is marked as redeemed after ``IRedeemer.redeem`` succeeds.
         """
         store = self.useFixture(TemporaryVoucherStore(get_config, lambda: now)).store
         controller = PaymentController(
             store,
-            DummyRedeemer(),
+            DummyRedeemer(public_key),
             default_token_count=10,
         )
         controller.redeem(voucher)
@@ -168,6 +169,7 @@ class PaymentControllerTests(TestCase):
             Equals(model_Redeemed(
                 finished=now,
                 token_count=10,
+                public_key=public_key,
             )),
         )
 
diff --git a/src/_zkapauthorizer/tests/test_model.py b/src/_zkapauthorizer/tests/test_model.py
index bf2dbaf7e3c4849ef8c8159450cb01ee7dca45ad..e4052d267459d17e0536dbb21b8c716f77e52b3d 100644
--- a/src/_zkapauthorizer/tests/test_model.py
+++ b/src/_zkapauthorizer/tests/test_model.py
@@ -392,6 +392,7 @@ class UnblindedTokenStoreTests(TestCase):
                 state=Equals(Redeemed(
                     finished=now,
                     token_count=num_tokens,
+                    public_key=public_key,
                 )),
             ),
         )