diff --git a/LICENSE b/LICENSE
index 54548ad294ca856618a0ed3509c084a6d05545e7..550a15e24ee76bbb129c6f82bae67b3f1f1050b0 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 BSD 3-Clause License
 
-Copyright (c) 2022, uAfrica Technologies (Pty) Ltd
+Copyright (c) 2023, Bob Group (Pty) Ltd
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/api_responses/api_responses.go b/api_responses/api_responses.go
index 768113b595f16cc1844461546d297473f3c42e93..bb2810a05f33f3a09002c6116f5bf5e4d456d3a9 100644
--- a/api_responses/api_responses.go
+++ b/api_responses/api_responses.go
@@ -2,8 +2,8 @@ package api_responses
 
 import (
 	"encoding/json"
-	"errors"
 	"fmt"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
 	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/map_utils"
 	"net/http"
 	"regexp"
@@ -58,7 +58,7 @@ func Error(err error, msg string, statusCode int) (events.APIGatewayProxyRespons
 		StatusCode: statusCode,
 		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
 		Body:       string(bodyBytes),
-	}, errors.New(msg)
+	}, errors.Error(msg)
 }
 
 func DatabaseServerErrorNew(err error, msg string) error {
@@ -87,7 +87,7 @@ func DatabaseServerErrorNew(err error, msg string) error {
 	}
 
 	return ServerErrorStruct{
-		error:   errors.New(errorString),
+		error:   errors.Error(errorString),
 		Message: msg,
 	}
 }
@@ -221,7 +221,7 @@ func ClientError(status int, message string) (events.APIGatewayProxyResponse, er
 	logs.WarnWithFields(map[string]interface{}{
 		"type": "Client error",
 		"code": status,
-	}, errors.New(message))
+	}, errors.Error(message))
 
 	e := errorMsg{
 		Message: message,
@@ -235,7 +235,7 @@ func ClientError(status int, message string) (events.APIGatewayProxyResponse, er
 		StatusCode: status,
 		Headers:    map_utils.MergeMaps(utils.CorsHeaders(), responses.ContentTypeJSONHeader),
 		Body:       string(b),
-	}, errors.New(message)
+	}, errors.Error(message)
 }
 
 func StatusCodeFromSQLError(err error) int {
diff --git a/audit/audit.go b/audit/audit.go
index 580513581cf6a07d8e1e2f08a9fdcff7fa8e6294..319083bc474ce03368a657dbab906a3ea7c8666d 100644
--- a/audit/audit.go
+++ b/audit/audit.go
@@ -24,14 +24,14 @@ func VerifyAuditEvents(original interface{}, new interface{}) error {
 	if original != nil {
 		structValue := reflect.ValueOf(original)
 		if structValue.Kind() != reflect.Struct {
-			return errors.New("original object is not of type struct")
+			return errors.Error("original object is not of type struct")
 		}
 	}
 
 	if new != nil {
 		structValue := reflect.ValueOf(new)
 		if structValue.Kind() != reflect.Struct {
-			return errors.New("new object is not of type struct")
+			return errors.Error("new object is not of type struct")
 		}
 	}
 
diff --git a/bank_transactions/absa_bank_transactions.go b/bank_transactions/absa_bank_transactions.go
index 1f2400bd3d0327c2b86bf1325225c4ad6dd249f9..7b22037b03fd0ae5855bcd81d70a7f0d15b128a9 100644
--- a/bank_transactions/absa_bank_transactions.go
+++ b/bank_transactions/absa_bank_transactions.go
@@ -219,7 +219,7 @@ func startStatementDownloadProcess(client *resty.Client) error {
 
 	// Ensure we are on the "Statement Enquiry" screen
 	if !strings.Contains(string(responseBytes), "Statement Enquiry") {
-		return errors.New("Could not load 'Statement Enquiry' screen")
+		return errors.Error("Could not load 'Statement Enquiry' screen")
 	}
 	time.Sleep(sleepTime)
 	return nil
diff --git a/cognito/cognito.go b/cognito/cognito.go
index ac79da6f825d272f7e2756c675a1ede4e2eeafae..3df6c2d9fcd36d2537329d3106a860bb45c70b78 100644
--- a/cognito/cognito.go
+++ b/cognito/cognito.go
@@ -146,7 +146,7 @@ func ConfirmPasswordReset(appClientID string, username string, password string,
 		return confirmPasswordReset(appClientID, username, password, res)
 	}
 
-	return nil, errors.New("User state not correct for confirmation. Please contact support.")
+	return nil, errors.Error("User state not correct for confirmation. Please contact support.")
 }
 
 // FOR API LOGS
diff --git a/encryption/encryption.go b/encryption/encryption.go
index 8d6c99a8ca49ee71f31c72e9f8c6e384dca39a17..bca0d801887ba00d6be2afa369fd4af6a097769b 100644
--- a/encryption/encryption.go
+++ b/encryption/encryption.go
@@ -51,7 +51,7 @@ func Md5HashString(bytesToHash []byte) string {
 
 func EncryptStruct(object any, key string) (string, error) {
 	if len(key) != 32 {
-		return "", errors.New("key should be 32 bytes")
+		return "", errors.Error("key should be 32 bytes")
 	}
 
 	block, err := aes.NewCipher([]byte(key))
@@ -80,7 +80,7 @@ func EncryptStruct(object any, key string) (string, error) {
 
 func DecryptStruct(encryptedStruct string, key string, object any) error {
 	if len(key) != 32 {
-		return errors.New("key should be 32 bytes")
+		return errors.Error("key should be 32 bytes")
 	}
 
 	decodedStruct, _ := base64.StdEncoding.DecodeString(encryptedStruct)
@@ -98,7 +98,7 @@ func DecryptStruct(encryptedStruct string, key string, object any) error {
 
 	nonceSize := aesGcm.NonceSize()
 	if len(decodedStructString) < nonceSize {
-		return errors.New("ciphertext too short")
+		return errors.Error("ciphertext too short")
 	}
 
 	nonce, ciphertext := decodedStructString[:nonceSize], decodedStructString[nonceSize:]
@@ -113,7 +113,7 @@ func DecryptStruct(encryptedStruct string, key string, object any) error {
 
 func EncryptByteArray(byteArray []byte, key string) (string, error) {
 	if len(key) != 32 {
-		return "", errors.New("key should be 32 bytes")
+		return "", errors.Error("key should be 32 bytes")
 	}
 
 	block, err := aes.NewCipher([]byte(key))
@@ -137,7 +137,7 @@ func EncryptByteArray(byteArray []byte, key string) (string, error) {
 
 func DecryptByteArray(encryptedByteArray []byte, key string, object any) error {
 	if len(key) != 32 {
-		return errors.New("key should be 32 bytes")
+		return errors.Error("key should be 32 bytes")
 	}
 
 	block, err := aes.NewCipher([]byte(key))
@@ -152,7 +152,7 @@ func DecryptByteArray(encryptedByteArray []byte, key string, object any) error {
 
 	nonceSize := aesGcm.NonceSize()
 	if len(encryptedByteArray) < nonceSize {
-		return errors.New("ciphertext too short")
+		return errors.Error("ciphertext too short")
 	}
 
 	nonce, ciphertext := encryptedByteArray[:nonceSize], encryptedByteArray[nonceSize:]
@@ -167,7 +167,7 @@ func DecryptByteArray(encryptedByteArray []byte, key string, object any) error {
 
 func Encrypt(plaintext string, key string) (string, error) {
 	if len(key) != 32 {
-		return "", errors.New("key should be 32 bytes")
+		return "", errors.Error("key should be 32 bytes")
 	}
 
 	block, err := aes.NewCipher([]byte(key))
@@ -190,7 +190,7 @@ func Encrypt(plaintext string, key string) (string, error) {
 
 func Decrypt(ciphertext string, key string) (string, error) {
 	if len(key) != 32 {
-		return "", errors.New("key should be 32 bytes")
+		return "", errors.Error("key should be 32 bytes")
 	}
 
 	block, err := aes.NewCipher([]byte(key))
@@ -205,7 +205,7 @@ func Decrypt(ciphertext string, key string) (string, error) {
 
 	nonceSize := aesGcm.NonceSize()
 	if len(ciphertext) < nonceSize {
-		return "", errors.New("ciphertext too short")
+		return "", errors.Error("ciphertext too short")
 	}
 
 	nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
diff --git a/errors/errors.go b/errors/errors.go
index bd6c2afebeb32fa6e72e7c8ef74685757ca87d92..d8245cc13167008099b56368b518e0261f225b5e 100644
--- a/errors/errors.go
+++ b/errors/errors.go
@@ -18,15 +18,6 @@ type ErrorWithIs interface {
 	Is(specificError error) bool
 }
 
-func New(message string) error {
-	err := &CustomError{
-		message: message,
-		caller:  GetCaller(2),
-		cause:   nil,
-	}
-	return err
-}
-
 func Error(message string) error {
 	err := &CustomError{
 		message: message,
diff --git a/handler_utils/handler.go b/handler_utils/handler.go
index 56a9af09ca3eb7b6abc1ea666c5297a0bf9a8dc0..be466ce58f90bd2ac1836da72764bd80cc548f86 100644
--- a/handler_utils/handler.go
+++ b/handler_utils/handler.go
@@ -49,11 +49,6 @@ func NewHandler(handlerFunction interface{}) (Handler, error) {
 			}
 		}
 
-		// todo: check special fields for claims, and see if also applies to params struct...
-		// AccountID must be int64 or *int64 with tag =???
-		// UserID must be int64 or *int64 with tag =???
-		// Username must be string with tag =???
-
 		h.RequestBodyType = handlerFunctionType.In(1)
 	}
 
diff --git a/logs/logs.go b/logs/logs.go
index f1b4c59aeaa9b30030770817e48c4a6d30c22bc1..42fa46c64c14bd464e7a637e1267a12bbea923ee 100644
--- a/logs/logs.go
+++ b/logs/logs.go
@@ -1,8 +1,8 @@
 package logs
 
 import (
-	"errors"
 	"fmt"
+	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
 	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/string_utils"
 	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/utils"
 	"net/http"
@@ -27,9 +27,6 @@ var isDebug = false
 var build string
 var raygunClient *raygun4go.Client
 
-// TODO
-// Sensitive word filtering
-
 // Password filtering
 var passwordRegex = regexp.MustCompile(`(?i:\\?"password\\?"\s*:\s*\\?"(.*)\\?",).*`)
 
@@ -191,7 +188,7 @@ func ErrorWithFields(fields map[string]interface{}, err error) {
 
 func ErrorWithMsg(message string, err error) {
 	if err == nil {
-		err = errors.New(message)
+		err = errors.Error(message)
 	}
 	ErrorWithFields(map[string]interface{}{
 		"message": message,
@@ -283,7 +280,7 @@ func sendRaygunError(fields map[string]interface{}, errToSend error) {
 	raygunClient.Request(fakeHttpRequest())
 
 	if errToSend == nil {
-		errToSend = errors.New("")
+		errToSend = errors.Error("")
 	}
 	err := raygunClient.SendError(errToSend)
 	if err != nil {
diff --git a/logs/stack.go b/logs/stack.go
deleted file mode 100644
index c64b584b8223759cc537b8772310730bccf161e7..0000000000000000000000000000000000000000
--- a/logs/stack.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package logs
-
-import (
-	"bufio"
-	"runtime/debug"
-	"strconv"
-	"strings"
-
-	"gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors"
-)
-
-type Stack struct {
-	Routine int64 // go routine nr that crashed...
-	Callers []errors.CallerInfo
-}
-
-func CallStack() Stack {
-	stack := Stack{
-		Callers: []errors.CallerInfo{},
-	}
-
-	// get the call stack
-	s := bufio.NewScanner(strings.NewReader(string(debug.Stack())))
-	// expect stack to look like this:
-	//   "goroutine 14 [running]:\nruntime/debug.Stack()\n\t/usr/local/go/src/runtime/debug/stack.go:24 +0x88\ngitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/api.Api.Handler.func3.1(0x1400009ff60)\n\t/Users/jansemmelink/uafrica/go-utils/api/lambda.go:210 +0x48\npanic({0x100780d20, 0x100b98a50})\n\t/usr/local/go/src/runtime/panic.go:1038 +0x21c\ngitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/examples/core/api/users.Crash({{{0x1008237e0, 0x1400040af30}, {0x4, {0x1008186e0, 0x14000010020}, 0x1400040af90, 0x25, 0x2f}, {0xc04f2cb513fbdc28, 0x103ccb5b4c, ...}}, ...}, ...)\n\t/Users/jansemmelink/uafrica/go-utils/examples/core/api/users/users.go:115 +0x20\nreflect.Value.call({0x10076bba0, 0x10080cdc8, 0x13}, {0x10059bb1c, 0x4}, {0x140000a0730, 0x2, 0x2})\n\t/usr/local/go/src/reflect/value.go:543 +0x584\nreflect.Value.Call({0x10076bba0, 0x10080cdc8, 0x13}, {0x140000a0730, 0x2, 0x2})\n\t/usr/local/go/src/reflect/value.go:339 +0x8c\ngitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/api.Api.Handler.func3({{0x10082f730, 0x100780960}, {0x0, 0x0}, {0x0, 0x0}, {0x10076bba0, 0x10080cdc8, 0x13}}, {0x140000a0730, ...})\n\t/Users/jansemmelink/uafrica/go-utils/api/lambda.go:214 +0x84\ngitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/api.Api.Handler({{0x10082b3f0, 0x140003b6480}, {0x10059b80a, 0x3}, {0x140003b6360}, {0x1005a3606, 0x12}, 0x10080cf98, {0x1008190e0, 0x100bdd308}, ...}, ...)\n\t/Users/jansemmelink/uafrica/go-utils/api/lambda.go:216 +0x1238\ngitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/api.Api.ServeHTTP({{0x10082b3f0, 0x140003b6480}, {0x10059b80a, 0x3}, {0x140003b6360}, {0x1005a3606, 0x12}, 0x10080cf98, {0x1008190e0, 0x100bdd308}, ...}, ...)\n\t/Users/jansemmelink/uafrica/go-utils/api/local.go:81 +0x6ac\nnet/http.serverHandler.ServeHTTP({0x1400015a620}, {0x1008200e8, 0x1400015aa80}, 0x140003c6900)\n\t/usr/local/go/src/net/http/server.go:2878 +0x444\nnet/http.(*conn).serve(0x14000279220, {0x1008237e0, 0x140003b6780})\n\t/usr/local/go/src/net/http/server.go:1929 +0xb6c\ncreated by net/http.(*Server).Serve\n\t/usr/local/go/src/net/http/server.go:3033 +0x4b8\n"
-	// i.e. multiple lines ending with "\n" each, e.g.:
-	// ------------------------------------------------------------------------------------------------------------
-	// goroutine 37 [running]:
-	// runtime/debug.Stack()
-	// 	/usr/local/go/src/runtime/debug/stack.go:24 +0x88
-	// gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/api.Api.Handler.func3.1(0x1400042cb00, 0x14000095f68)
-	// 	/Users/jansemmelink/uafrica/go-utils/api/lambda.go:216 +0x50
-	// panic({0x100c08d20, 0x101020a70})
-	// 	/usr/local/go/src/runtime/panic.go:1038 +0x21c
-	// gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/examples/core/api/users.Crash({{{0x100cab7c0, 0x140004843c0}, {0x4, {0x100ca06c0, 0x14000138010}, 0x14000484420, 0x20, 0x28}, {0xc04f2d6c3023b330, 0x227861e6a, ...}}, ...}, ...)
-	// 	/Users/jansemmelink/uafrica/go-utils/examples/core/api/users/users.go:115 +0x20
-	// ...
-	// ------------------------------------------------------------------------------------------------------------
-
-	// get go routine nr from first line: "gorouting <nr> [running]:"
-	if s.Scan() {
-		p := strings.SplitN(s.Text(), " ", 3)
-		if len(p) >= 2 && p[0] == "goroutine" {
-			routineNr, err := strconv.ParseInt(p[1], 10, 64)
-			if err == nil {
-				stack.Routine = routineNr
-			}
-		}
-	}
-
-	// next expect line pairs for each level of the stack
-	for {
-		// read first line in this pair, expecting <package>.<funcName>(<args>)
-		if !s.Scan() {
-			break
-		}
-		line1 := s.Text()
-
-		if !s.Scan() {
-			break
-		}
-		line2 := s.Text()
-
-		// fmt.Printf("  STACK LINE: %s %s\n", line1, line2)
-
-		// split line 1 on any bracket or comma to get "<package>.<funcName>"["<arg>" ...]
-		// func may have multiple '.', so do not split on that yet!
-		line1Fields := strings.FieldsFunc(line1, func(c rune) bool { return strings.Contains("(), ", string(c)) })
-
-		// split line 2 <file>:<line> +0x##...
-		line2Fields := strings.FieldsFunc(line2, func(c rune) bool { return strings.Contains(": ", string(c)) })
-		lineNr, _ := strconv.ParseInt(line2Fields[1], 10, 64)
-		caller := errors.NewCaller(line1Fields[0], line2Fields[0], int(lineNr))
-
-		// skip first levels that refer to capturing the stack
-		ci := caller.Info()
-		if len(stack.Callers) == 0 {
-			if ci.Package == "runtime/debug" ||
-				ci.Package == "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/logs" ||
-				ci.Package == "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/errors" ||
-				ci.Package == "" {
-				continue
-			}
-			if _, err := strconv.ParseInt(ci.Function, 10, 64); err == nil {
-				continue // typical defer function without a name used to catch the crash
-			}
-		}
-		stack.Callers = append(stack.Callers, caller.Info())
-	}
-	return stack
-}
diff --git a/string_utils/string_utils.go b/string_utils/string_utils.go
index 995e94a309eabbf18f1bee3cd9462242d930a2c4..9a36862709b8a8d78a3b09e20c5ea78c5443a68d 100644
--- a/string_utils/string_utils.go
+++ b/string_utils/string_utils.go
@@ -200,6 +200,13 @@ func SentenceCase(str string) string {
 	return ""
 }
 
+func Capitalize(s string) string {
+	if len(s) == 0 {
+		return s
+	}
+	return string(unicode.ToUpper(rune(s[0]))) + s[1:]
+}
+
 // SplitString separates a string on any character in the list of sep
 func SplitString(str string, sep []rune) []string {
 	splitStrings := strings.FieldsFunc(str, func(c rune) bool {
diff --git a/string_utils/string_utils_test.go b/string_utils/string_utils_test.go
index 3c52e65fd4ae909cc7a7dda123d7d8d3c4adbca6..04da56723ed34dc3cc054a82b69d8ba4a8fb9fab 100644
--- a/string_utils/string_utils_test.go
+++ b/string_utils/string_utils_test.go
@@ -13,17 +13,17 @@ func TestIsValidEmail(t *testing.T) {
 	}{
 		{
 			name: "valid",
-			args: args{email: "johandk@uafrica.com"},
+			args: args{email: "johan@bob.co.za"},
 			want: true,
 		},
 		{
 			name: "invalid",
-			args: args{email: "johandk@@uafrica.com"},
+			args: args{email: "johan@bob.co.za"},
 			want: false,
 		},
 		{
 			name: "invalid",
-			args: args{email: "johandk@uafricacom"},
+			args: args{email: "johan@bob.co.za"},
 			want: false,
 		},
 	}
diff --git a/struct_utils/named_values_to_struct.go b/struct_utils/named_values_to_struct.go
index 3482c22c3b460a587e00c4adc39274031c108ea6..97861207876e14ebce9cace94dadc1d10576825a 100644
--- a/struct_utils/named_values_to_struct.go
+++ b/struct_utils/named_values_to_struct.go
@@ -15,12 +15,15 @@ import (
 )
 
 // Purpose:
+//
 //	Make a list of named values from the env for parsing into a struct
 //
 // Parameters:
+//
 //	prefix should be uppercase (by convention) env prefix like "MY_LIB_CONFIG", without trailing "_"
 //
 // Result:
+//
 //	named values that can be passed into UnmarshalNamedValues()
 //
 // All env starting with "<prefix>_" will be copied without "<prefix>_"
@@ -110,7 +113,8 @@ type nrWithValues struct {
 
 // converts query string params to named values that can be parsed into a struct
 // it support both single/multi-value params, depending how you get them from your HTTP library
-//    (e.g. AWS API Gateway Context returns both but default golang net/http returns only params)
+//
+//	(e.g. AWS API Gateway Context returns both but default golang net/http returns only params)
 func NamedValuesFromURL(params map[string]string, multiValueParams map[string][]string) map[string][]string {
 	result := map[string][]string{}
 	for n, v := range params {
@@ -161,34 +165,36 @@ func NamedValuesFromURL(params map[string]string, multiValueParams map[string][]
 }
 
 // Purpose:
-// 	UnmarshalNamedValues() parses a set of named values into a struct using json tag matching
-//  Unlike json.Unmarshal(), it takes care of converting quoted "true" -> true, "1" -> int(1) etc...
 //
-//	Typically used to parse environment or URL params into a struct
-//	because normal json.Unmarshal() will fail to parse "2" into an integer etc
+//		UnmarshalNamedValues() parses a set of named values into a struct using json tag matching
+//	 Unlike json.Unmarshal(), it takes care of converting quoted "true" -> true, "1" -> int(1) etc...
+//
+//		Typically used to parse environment or URL params into a struct
+//		because normal json.Unmarshal() will fail to parse "2" into an integer etc
 //
-//	By convention, the names should be lowercase to match json tag with "_" delimeters
-//	And also use "_" for nested sub-struct names
-//	  named value "a_b_c_d":5 would be stored in
-//    field with json tag "a_b_c_d" or
-//	  field with json tag "a_b"        which is a struct with a json tagged field "c_d" etc...
+//		By convention, the names should be lowercase to match json tag with "_" delimeters
+//		And also use "_" for nested sub-struct names
+//		  named value "a_b_c_d":5 would be stored in
+//	   field with json tag "a_b_c_d" or
+//		  field with json tag "a_b"        which is a struct with a json tagged field "c_d" etc...
 //
 // Parameters:
-// 	namedValues is name-value pairs, typical from URL params or OS environment
+//
+//	namedValues is name-value pairs, typical from URL params or OS environment
 //		see construction functions for this:
 //			NamedValuesFromEnv()
 //			NamedValuesFromURL()
 //
-// 	structPtr must be ptr to a struct variable
+//	structPtr must be ptr to a struct variable
 //		undefined values will not be changed, so you can call this multiple times on the
 //		same struct to amend a few values, leaving the rest and default values unchanged
 //
 // Return:
+//
 //	unused values
-// 	nil or error if some values could not be used
+//	nil or error if some values could not be used
 //
 //	If all values must be used, check len(unusedValues) when err==nil
-//
 func UnmarshalNamedValues(namedValues map[string][]string, structPtr interface{}) (unusedValues map[string][]string, err error) {
 	if structPtr == nil {
 		return nil, errors.Errorf("cannot unmarshal into nil")
@@ -263,8 +269,6 @@ func unmarshalNamedValuesIntoStructPtr(prefix string, namedValues map[string][]s
 					return
 				}
 				structPtrFieldValue.Set(reflect.Append(structPtrFieldValue, parsedValue))
-
-				// todo: sorting of list using value names as applicable
 			}
 		} else {
 			// field is not a slice, expecting only a single value