diff --git a/cmd/africastalking/main.go b/cmd/africastalking/main.go
index 7d1caa8..98864db 100644
--- a/cmd/africastalking/main.go
+++ b/cmd/africastalking/main.go
@@ -7,7 +7,6 @@ import (
 	"flag"
 	"fmt"
 	"io"
-	"log"
 	"net/http"
 	"os"
 	"os/signal"
@@ -45,14 +44,14 @@ type atRequestParser struct{}
 func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
 	rqv, ok := rq.(*http.Request)
 	if !ok {
-		log.Printf("got an invalid request:", rq)
+		logg.Warnf("got an invalid request", "req", rq)
 		return "", handlers.ErrInvalidRequest
 	}
 
 	// Capture body (if any) for logging
 	body, err := io.ReadAll(rqv.Body)
 	if err != nil {
-		log.Printf("failed to read request body:", err)
+		logg.Warnf("failed to read request body", "err", err)
 		return "", fmt.Errorf("failed to read request body: %v", err)
 	}
 	// Reset the body for further reading
@@ -62,13 +61,13 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
 	bodyLog := map[string]string{"body": string(body)}
 	logBytes, err := json.Marshal(bodyLog)
 	if err != nil {
-		log.Printf("failed to marshal request body:", err)
+		logg.Warnf("failed to marshal request body", "err", err)
 	} else {
-		log.Printf("Received request:", string(logBytes))
+		logg.Debugf("received request", "bytes", logBytes)
 	}
 
 	if err := rqv.ParseForm(); err != nil {
-		log.Printf("failed to parse form data: %v", err)
+		logg.Warnf("failed to parse form data", "err", err)
 		return "", fmt.Errorf("failed to parse form data: %v", err)
 	}
 
@@ -79,7 +78,7 @@ func (arp *atRequestParser) GetSessionId(rq any) (string, error) {
 
 	formattedNumber, err := common.FormatPhoneNumber(phoneNumber)
 	if err != nil {
-		fmt.Printf("Error: %v\n", err)
+		logg.Warnf("failed to format phone number", "err", err)
 		return "", fmt.Errorf("failed to format number")
 	}
 
diff --git a/common/db.go b/common/db.go
index 5532dc9..a5cf1c1 100644
--- a/common/db.go
+++ b/common/db.go
@@ -7,43 +7,83 @@ import (
 	"git.defalsify.org/vise.git/logging"
 )
 
+// DataType is a subprefix value used in association with vise/db.DATATYPE_USERDATA. 
+//
+// All keys are used only within the context of a single account. Unless otherwise specified, the user context is the session id.
+//
+// * The first byte is vise/db.DATATYPE_USERDATA
+// * The last 2 bytes are the DataTyp value, big-endian.
+// * The intermediate bytes are the id of the user context.
+//
+// All values are strings
 type DataTyp uint16
 
 const (
-	DATA_ACCOUNT DataTyp = iota
-	DATA_ACCOUNT_CREATED
-	DATA_TRACKING_ID
+	// API Tracking id to follow status of account creation
+	DATA_TRACKING_ID = iota
+	// EVM address returned from API on account creation
 	DATA_PUBLIC_KEY
-	DATA_CUSTODIAL_ID
+	// Currently active PIN used to authenticate ussd state change requests
 	DATA_ACCOUNT_PIN
-	DATA_ACCOUNT_STATUS
+	// The first name of the user
 	DATA_FIRST_NAME
+	// The last name of the user
 	DATA_FAMILY_NAME
+	// The year-of-birth of the user
 	DATA_YOB
+	// The location of the user
 	DATA_LOCATION
+	// The gender of the user
 	DATA_GENDER
+	// The offerings description of the user
 	DATA_OFFERINGS
+	// The ethereum address of the recipient of an ongoing send request
 	DATA_RECIPIENT
+	// The voucher value amount of an ongoing send request
 	DATA_AMOUNT
+	// A general swap field for temporary values
 	DATA_TEMPORARY_VALUE
+	// Currently active voucher symbol of user
 	DATA_ACTIVE_SYM
+	// Voucher balance of user's currently active voucher
 	DATA_ACTIVE_BAL
+	// String boolean indicating whether use of PIN is blocked
 	DATA_BLOCKED_NUMBER
+	// Reverse mapping of a user's evm address to a session id.
 	DATA_PUBLIC_KEY_REVERSE
+	// Decimal count of the currently active voucher
 	DATA_ACTIVE_DECIMAL
+	// EVM address of the currently active voucher
 	DATA_ACTIVE_ADDRESS
-	// Start the sub prefix data at 256 (0x0100)
+)
+
+const (
+	// List of valid voucher symbols in the user context.
 	DATA_VOUCHER_SYMBOLS DataTyp = 256 + iota
+	// List of voucher balances for vouchers valid in the user context.
 	DATA_VOUCHER_BALANCES
+	// List of voucher decimal counts for vouchers valid in the user context.
 	DATA_VOUCHER_DECIMALS
+	// List of voucher EVM addresses for vouchers valid in the user context.
 	DATA_VOUCHER_ADDRESSES
-	DATA_TX_SENDERS
+	// List of senders for valid transactions in the user context.
+)
+
+const (
+	DATA_TX_SENDERS = 512 + iota
+	// List of recipients for valid transactions in the user context.
 	DATA_TX_RECIPIENTS
+	// List of voucher values for valid transactions in the user context.
 	DATA_TX_VALUES
+	// List of voucher EVM addresses for valid transactions in the user context.
 	DATA_TX_ADDRESSES
+	// List of valid transaction hashes in the user context.
 	DATA_TX_HASHES
+	// List of transaction dates for valid transactions in the user context.
 	DATA_TX_DATES
+	// List of voucher symbols for valid transactions in the user context.
 	DATA_TX_SYMBOLS
+	// List of voucher decimal counts for valid transactions in the user context.
 	DATA_TX_DECIMALS
 )
 
diff --git a/common/user_store.go b/common/user_store.go
index 29796e2..6c770d8 100644
--- a/common/user_store.go
+++ b/common/user_store.go
@@ -20,7 +20,7 @@ type UserDataStore struct {
 func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ DataTyp) ([]byte, error) {
 	store.SetPrefix(db.DATATYPE_USERDATA)
 	store.SetSession(sessionId)
-	k := PackKey(typ, []byte(sessionId))
+	k := ToBytes(typ)
 	return store.Get(ctx, k)
 }
 
@@ -29,6 +29,6 @@ func (store *UserDataStore) ReadEntry(ctx context.Context, sessionId string, typ
 func (store *UserDataStore) WriteEntry(ctx context.Context, sessionId string, typ DataTyp, value []byte) error {
 	store.SetPrefix(db.DATATYPE_USERDATA)
 	store.SetSession(sessionId)
-	k := PackKey(typ, []byte(sessionId))
+	k := ToBytes(typ)
 	return store.Put(ctx, k, value)
 }
diff --git a/debug/cap.go b/debug/cap.go
new file mode 100644
index 0000000..458bb48
--- /dev/null
+++ b/debug/cap.go
@@ -0,0 +1,5 @@
+package debug
+
+var (
+	DebugCap uint32
+)
diff --git a/debug/db.go b/debug/db.go
new file mode 100644
index 0000000..ec9e58f
--- /dev/null
+++ b/debug/db.go
@@ -0,0 +1,84 @@
+package debug
+
+import (
+	"fmt"
+	"encoding/binary"
+
+	"git.grassecon.net/urdt/ussd/common"
+	"git.defalsify.org/vise.git/db"
+)
+
+var (
+	dbTypStr map[common.DataTyp]string = make(map[common.DataTyp]string)
+)
+
+type KeyInfo struct {
+	SessionId string
+	Typ uint8
+	SubTyp common.DataTyp
+	Label string
+	Description string
+}
+
+func (k KeyInfo) String() string {
+	v := uint16(k.SubTyp)
+	s := subTypToString(k.SubTyp)
+	if s == "" {
+		v = uint16(k.Typ)
+		s = typToString(k.Typ)
+	}
+	return fmt.Sprintf("Session Id: %s\nTyp: %s (%d)\n", k.SessionId, s, v)
+}
+
+func ToKeyInfo(k []byte, sessionId string) (KeyInfo, error) {
+	o := KeyInfo{}
+	b := []byte(sessionId)
+
+	if len(k) <= len(b) {
+		return o, fmt.Errorf("storage key missing")
+	}
+
+	o.SessionId = sessionId
+
+	o.Typ = uint8(k[0])
+	k = k[1:]
+	o.SessionId = string(k[:len(b)])
+	k = k[len(b):]
+
+	if o.Typ == db.DATATYPE_USERDATA {
+		if len(k) == 0 {
+			return o, fmt.Errorf("missing subtype key")
+		}
+		v := binary.BigEndian.Uint16(k[:2])
+		o.SubTyp = common.DataTyp(v)
+		o.Label = subTypToString(o.SubTyp)
+		k = k[2:]
+	} else {
+		o.Label = typToString(o.Typ)
+	}
+
+	if len(k) != 0 {
+		return o, fmt.Errorf("excess key information")
+	}
+
+	return o, nil
+}
+
+func FromKey(k []byte) (KeyInfo, error) {
+	o := KeyInfo{}
+
+	if len(k) < 4 {
+		return o, fmt.Errorf("insufficient key length")
+	}
+
+	sessionIdBytes := k[1:len(k)-2]
+	return ToKeyInfo(k, string(sessionIdBytes))
+}
+
+func subTypToString(v common.DataTyp) string {
+	return dbTypStr[v + db.DATATYPE_USERDATA + 1]
+}
+
+func typToString(v uint8) string {
+	return dbTypStr[common.DataTyp(uint16(v))]
+}
diff --git a/debug/db_debug.go b/debug/db_debug.go
new file mode 100644
index 0000000..ed2dd66
--- /dev/null
+++ b/debug/db_debug.go
@@ -0,0 +1,48 @@
+// +build debugdb
+
+package debug
+
+import (
+	"git.defalsify.org/vise.git/db"
+
+	"git.grassecon.net/urdt/ussd/common"
+)
+
+func init() {
+	DebugCap |= 1
+	dbTypStr[db.DATATYPE_STATE] = "internal state"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT] = "account"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_CREATED] = "account created"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TRACKING_ID] = "tracking id"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY] = "public key"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_CUSTODIAL_ID] = "custodial id"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_PIN] = "account pin"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACCOUNT_STATUS] = "account status"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FIRST_NAME] = "first name"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_FAMILY_NAME] = "family name"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_YOB] = "year of birth"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_LOCATION] = "location"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_GENDER] = "gender"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_OFFERINGS] = "offerings"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_RECIPIENT] = "recipient"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_AMOUNT] = "amount"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TEMPORARY_VALUE] = "temporary value"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_SYM] = "active sym"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_BAL] = "active bal"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_BLOCKED_NUMBER] = "blocked number"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_PUBLIC_KEY_REVERSE] = "public_key_reverse"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_DECIMAL] = "active decimal"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_ACTIVE_ADDRESS] = "active address"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_SYMBOLS] = "voucher symbols"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_BALANCES] = "voucher balances"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_DECIMALS] = "voucher decimals"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_VOUCHER_ADDRESSES] = "voucher addresses"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_SENDERS] = "tx senders"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_RECIPIENTS] = "tx recipients"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_VALUES] = "tx values"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_ADDRESSES] = "tx addresses"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_HASHES] = "tx hashes"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_DATES] = "tx dates"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_SYMBOLS] = "tx symbols"
+	dbTypStr[db.DATATYPE_USERDATA + 1 + common.DATA_TX_DECIMALS] = "tx decimals"
+}
diff --git a/debug/db_test.go b/debug/db_test.go
new file mode 100644
index 0000000..25f781d
--- /dev/null
+++ b/debug/db_test.go
@@ -0,0 +1,78 @@
+package debug
+
+import (
+	"testing"
+
+	"git.grassecon.net/urdt/ussd/common"
+	"git.defalsify.org/vise.git/db"
+)
+
+func TestDebugDbSubKeyInfo(t *testing.T) {
+	s := "foo"
+	b := []byte{0x20}
+	b = append(b, []byte(s)...)
+	b = append(b, []byte{0x00, 0x02}...)
+	r, err := ToKeyInfo(b, s)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if r.SessionId != s {
+		t.Fatalf("expected %s, got %s", s, r.SessionId)
+	}
+	if r.Typ != 32 {
+		t.Fatalf("expected 64, got %d", r.Typ)
+	}
+	if r.SubTyp != 2 {
+		t.Fatalf("expected 2, got %d", r.SubTyp)
+	}
+	if DebugCap & 1 > 0 {
+		if r.Label != "tracking id" {
+			t.Fatalf("expected 'tracking id', got '%s'", r.Label)
+		}
+	}
+}
+
+func TestDebugDbKeyInfo(t *testing.T) {
+	s := "bar"
+	b := []byte{0x10}
+	b = append(b, []byte(s)...)
+	r, err := ToKeyInfo(b, s)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if r.SessionId != s {
+		t.Fatalf("expected %s, got %s", s, r.SessionId)
+	}
+	if r.Typ != 16 {
+		t.Fatalf("expected 16, got %d", r.Typ)
+	}
+	if DebugCap & 1 > 0 {
+		if r.Label != "internal state" {
+			t.Fatalf("expected 'internal_state', got '%s'", r.Label)
+		}
+	}
+}
+
+func TestDebugDbKeyInfoRestore(t *testing.T) {
+	s := "bar"
+	b := []byte{db.DATATYPE_USERDATA}
+	b = append(b, []byte(s)...)
+	k := common.ToBytes(common.DATA_ACTIVE_SYM)
+	b = append(b, k...)
+
+	r, err := ToKeyInfo(b, s)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if r.SessionId != s {
+		t.Fatalf("expected %s, got %s", s, r.SessionId)
+	}
+	if r.Typ != 32 {
+		t.Fatalf("expected 32, got %d", r.Typ)
+	}
+	if DebugCap & 1 > 0 {
+		if r.Label != "active sym" {
+			t.Fatalf("expected 'active sym', got '%s'", r.Label)
+		}
+	}
+}
diff --git a/devtools/gen/main.go b/devtools/gen/main.go
new file mode 100644
index 0000000..b9e2aed
--- /dev/null
+++ b/devtools/gen/main.go
@@ -0,0 +1,79 @@
+package main
+
+import (
+	"context"
+	"crypto/sha1"
+	"flag"
+	"fmt"
+	"os"
+	"path"
+
+	"git.defalsify.org/vise.git/logging"
+	"git.grassecon.net/urdt/ussd/config"
+	"git.grassecon.net/urdt/ussd/internal/storage"
+	"git.grassecon.net/urdt/ussd/initializers"
+	"git.grassecon.net/urdt/ussd/common"
+)
+
+var (
+	logg      = logging.NewVanilla()
+	scriptDir = path.Join("services", "registration")
+)
+
+func init() {
+	initializers.LoadEnvVariables()
+}
+
+
+func main() {
+	config.LoadConfig()
+
+	var dbDir string
+	var sessionId string
+	var database string
+	var engineDebug bool
+
+	flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
+	flag.StringVar(&database, "db", "gdbm", "database to be used")
+	flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
+	flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
+	flag.Parse()
+
+	ctx := context.Background()
+	ctx = context.WithValue(ctx, "SessionId", sessionId)
+	ctx = context.WithValue(ctx, "Database", database)
+
+	resourceDir := scriptDir
+	menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
+
+	store, err := menuStorageService.GetUserdataDb(ctx)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+	userStore := common.UserDataStore{store}
+
+	h := sha1.New()
+	h.Write([]byte(sessionId))
+	address := h.Sum(nil)
+	addressString := fmt.Sprintf("%x", address)
+
+	err = userStore.WriteEntry(ctx, sessionId, common.DATA_PUBLIC_KEY, []byte(addressString))
+	if err != nil {
+		fmt.Fprintf(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+
+	err = userStore.WriteEntry(ctx, addressString, common.DATA_PUBLIC_KEY_REVERSE, []byte(sessionId))
+	if err != nil {
+		fmt.Fprintf(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+
+	err = store.Close()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+	
+}
diff --git a/devtools/store/main.go b/devtools/store/main.go
new file mode 100644
index 0000000..9262703
--- /dev/null
+++ b/devtools/store/main.go
@@ -0,0 +1,78 @@
+package main
+
+import (
+	"context"
+	"flag"
+	"fmt"
+	"os"
+	"path"
+
+	"git.grassecon.net/urdt/ussd/config"
+	"git.grassecon.net/urdt/ussd/initializers"
+	"git.grassecon.net/urdt/ussd/internal/storage"
+	"git.grassecon.net/urdt/ussd/debug"
+	"git.defalsify.org/vise.git/logging"
+)
+
+var (
+	logg      = logging.NewVanilla()
+	scriptDir = path.Join("services", "registration")
+)
+
+func init() {
+	initializers.LoadEnvVariables()
+}
+
+
+func main() {
+	config.LoadConfig()
+
+	var dbDir string
+	var sessionId string
+	var database string
+	var engineDebug bool
+
+	flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
+	flag.StringVar(&database, "db", "gdbm", "database to be used")
+	flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
+	flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
+	flag.Parse()
+
+	ctx := context.Background()
+	ctx = context.WithValue(ctx, "SessionId", sessionId)
+	ctx = context.WithValue(ctx, "Database", database)
+
+	resourceDir := scriptDir
+	menuStorageService := storage.NewMenuStorageService(dbDir, resourceDir)
+
+	store, err := menuStorageService.GetUserdataDb(ctx)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+
+	d, err := store.Dump(ctx, []byte(sessionId))
+	if err != nil {
+		fmt.Fprintf(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+
+	for true {
+		k, v := d.Next(ctx)
+		if k == nil {
+			break
+		}
+		o, err := debug.FromKey(k)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, err.Error())
+			os.Exit(1)
+		}
+		fmt.Printf("%vValue: %v\n\n", o, v)
+	}
+
+	err = store.Close()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, err.Error())
+		os.Exit(1)
+	}
+}
diff --git a/doc/data.md b/doc/data.md
new file mode 100644
index 0000000..7a41f0c
--- /dev/null
+++ b/doc/data.md
@@ -0,0 +1,28 @@
+# Internals
+
+## Version
+
+This document describes component versions:
+
+* `urdt-ussd` `v0.5.0-beta`
+* `go-vise` `v0.2.2`
+
+
+## User profile data
+
+All user profile items are stored under keys matching the user's session id, prefixed with the 8-bit value `git.defalsify.org/vise.git/db.DATATYPE_USERDATA` (32), and followed with a 16-big big-endian value subprefix.
+
+For example, given the sessionId `+254123` and the key `git.grassecon.net/urdt-ussd/common.DATA_PUBLIC_KEY` (2) will be stored under the key:
+
+```
+0x322b3235343132330002
+
+prefix   sessionid       subprefix
+32       2b323534313233  0002
+```
+
+### Sub-prefixes
+
+All sub-prefixes are defined as constants in the `git.grassecon.net/urdt-ussd/common` package. The constant names have the prefix `DATA_`
+
+Please refer to inline godoc documentation for the `git.grassecon.net/urdt-ussd/common` package for details on each data item.
diff --git a/go.mod b/go.mod
index d89f0f3..e1b7ddb 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module git.grassecon.net/urdt/ussd
 go 1.23.0
 
 require (
-	git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a
+	git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80
 	github.com/alecthomas/assert/v2 v2.2.2
 	github.com/gofrs/uuid v4.4.0+incompatible
 	github.com/grassrootseconomics/eth-custodial v1.3.0-beta
diff --git a/go.sum b/go.sum
index 5532c1c..ef7b782 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,5 @@
-git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a h1:LvGKktk0kUnuRN3nF9r15D8OoV0sFaMmQr52kGq2gtE=
-git.defalsify.org/vise.git v0.2.1-0.20241122120231-9e9ee5bdfa7a/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
+git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80 h1:GYUVXRUtMpA40T4COeAduoay6CIgXjD5cfDYZOTFIKw=
+git.defalsify.org/vise.git v0.2.1-0.20241212145627-683015d4df80/go.mod h1:jyBMe1qTYUz3mmuoC9JQ/TvFeW0vTanCUcPu3H8p4Ck=
 github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
 github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
 github.com/alecthomas/participle/v2 v2.0.0 h1:Fgrq+MbuSsJwIkw3fEj9h75vDP0Er5JzepJ0/HNHv0g=
diff --git a/internal/handlers/ussd/menuhandler.go b/internal/handlers/ussd/menuhandler.go
index c2b41b2..b3c6f1d 100644
--- a/internal/handlers/ussd/menuhandler.go
+++ b/internal/handlers/ussd/menuhandler.go
@@ -226,7 +226,7 @@ func (h *Handlers) CreateAccount(ctx context.Context, sym string, input []byte)
 		return res, fmt.Errorf("missing session")
 	}
 	store := h.userdataStore
-	_, err = store.ReadEntry(ctx, sessionId, common.DATA_ACCOUNT_CREATED)
+	_, err = store.ReadEntry(ctx, sessionId, common.DATA_PUBLIC_KEY)
 	if err != nil {
 		if db.IsNotFound(err) {
 			logg.InfoCtxf(ctx, "Creating an account because it doesn't exist")
diff --git a/internal/storage/gdbm.go b/internal/storage/gdbm.go
index 11979e1..31ebf47 100644
--- a/internal/storage/gdbm.go
+++ b/internal/storage/gdbm.go
@@ -115,7 +115,8 @@ func(tdb *ThreadGdbmDb) Close() error {
 	return err
 }
 
-func(tdb *ThreadGdbmDb) Dump(_ context.Context, _ []byte) (*db.Dumper, error) {
-	logg.Warnf("method not implemented for thread gdbm db")
-	return nil, nil
+func(tdb *ThreadGdbmDb) Dump(ctx context.Context, key []byte) (*db.Dumper, error) {
+	tdb.reserve()
+	defer tdb.release()
+	return tdb.db.Dump(ctx, key)
 }
diff --git a/menutraversal_test/test_setup.json b/menutraversal_test/test_setup.json
index 1f9d61d..c5860b4 100644
--- a/menutraversal_test/test_setup.json
+++ b/menutraversal_test/test_setup.json
@@ -11,7 +11,7 @@
                     },
                     {
                         "input": "0",
-                        "expectedContent": "Do you agree to terms and conditions?\n0:Yes\n1:No"
+                        "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No"
                     },
                     {
                         "input": "0",
@@ -44,7 +44,7 @@
                     },
                     {
                         "input": "0",
-                        "expectedContent": "Do you agree to terms and conditions?\n0:Yes\n1:No"
+                        "expectedContent": "Do you agree to terms and conditions?\nhttps://grassecon.org/pages/terms-and-conditions\n\n0:Yes\n1:No"
                     },
                     {
                         "input": "1",
diff --git a/services/registration/terms b/services/registration/terms
index 05b8c11..8af5b06 100644
--- a/services/registration/terms
+++ b/services/registration/terms
@@ -1 +1,2 @@
-Do you agree to terms and conditions?
\ No newline at end of file
+Do you agree to terms and conditions?
+https://grassecon.org/pages/terms-and-conditions
diff --git a/services/registration/terms_swa b/services/registration/terms_swa
index 7113cd7..5678186 100644
--- a/services/registration/terms_swa
+++ b/services/registration/terms_swa
@@ -1 +1,2 @@
-Kwa kutumia hii huduma umekubali sheria na masharti?
\ No newline at end of file
+Kwa kutumia hii huduma umekubali sheria na masharti?
+https://grassecon.org/pages/terms-and-conditions