diff --git a/src/libs/crypto/vc/index.ts b/src/libs/crypto/vc/index.ts
index 889103bcc..252ca06df 100644
--- a/src/libs/crypto/vc/index.ts
+++ b/src/libs/crypto/vc/index.ts
@@ -9,7 +9,6 @@
 import { Buffer } from "buffer/";
 import * as didJwt from "did-jwt";
 import { JWTVerified } from "did-jwt";
-import { JWTDecoded } from "did-jwt/lib/JWT";
 import { Resolver } from "did-resolver";
 import { IIdentifier } from "@veramo/core";
 import * as u8a from "uint8arrays";
@@ -41,7 +40,7 @@ export interface KeyMeta {
   passkeyCredIdHex?: string;
 }
 
-const resolver = new Resolver({ ethr: didEthLocalResolver });
+const ethLocalResolver = new Resolver({ ethr: didEthLocalResolver });
 
 /**
  * Tell whether a key is from a passkey
@@ -62,6 +61,7 @@ export async function createEndorserJwtForKey(
     const privateKeyHex = identity.keys[0].privateKeyHex;
     const signer = await SimpleSigner(privateKeyHex as string);
     const options = {
+      // alg: "ES256K", // "K" is the default, "K-R" is used by the server in tests
       issuer: account.did,
       signer: signer,
       expiresIn: undefined as number | undefined,
@@ -124,7 +124,8 @@ function bytesToHex(b: Uint8Array): string {
 }
 
 // We should be calling 'verify' in more places, showing warnings if it fails.
-export function decodeEndorserJwt(jwt: string): JWTDecoded {
+// @returns JWTDecoded with { header: JWTHeader, payload: string, signature: string, data: string } (but doesn't verify the signature)
+export function decodeEndorserJwt(jwt: string) {
   return didJwt.decodeJWT(jwt);
 }
 
@@ -134,10 +135,8 @@ export async function decodeAndVerifyJwt(
   jwt: string,
 ): Promise<Omit<JWTVerified, "didResolutionResult" | "signer" | "jwt">> {
   const pieces = jwt.split(".");
-  console.log("WTF decodeAndVerifyJwt", typeof jwt, jwt, pieces);
   const header = JSON.parse(base64urlDecodeString(pieces[0]));
   const payload = JSON.parse(base64urlDecodeString(pieces[1]));
-  console.log("WTF decodeAndVerifyJwt after", header, payload);
   const issuerDid = payload.iss;
   if (!issuerDid) {
     return Promise.reject({
@@ -149,7 +148,9 @@ export async function decodeAndVerifyJwt(
 
   if (issuerDid.startsWith(ETHR_DID_PREFIX)) {
     try {
-      const verified = await didJwt.verifyJWT(jwt, { resolver });
+      const verified = await didJwt.verifyJWT(jwt, {
+        resolver: ethLocalResolver,
+      });
       return verified;
     } catch (e: unknown) {
       return Promise.reject({
diff --git a/src/router/index.ts b/src/router/index.ts
index 5c198c3cc..ba4b3a244 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -70,7 +70,7 @@ const routes: Array<RouteRecordRaw> = [
     component: () => import("../views/ContactGiftingView.vue"),
   },
   {
-    path: "/contact-import",
+    path: "/contact-import/:jwt?",
     name: "contact-import",
     component: () => import("../views/ContactImportView.vue"),
   },
diff --git a/src/views/ContactImportView.vue b/src/views/ContactImportView.vue
index b7ec1ba1d..9a7a5cb3d 100644
--- a/src/views/ContactImportView.vue
+++ b/src/views/ContactImportView.vue
@@ -16,10 +16,11 @@
       Contact Import
     </h1>
 
-    <span class="flex justify-center">
+    <span v-if="contactsImporting.length > 0" class="flex justify-center">
       <input type="checkbox" v-model="makeVisible" class="mr-2" />
       Make my activity visible to these contacts.
     </span>
+
     <div v-if="sameCount > 0">
       <span v-if="sameCount == 1"
         >One contact is the same as an existing contact</span
@@ -85,17 +86,19 @@
 </template>
 
 <script lang="ts">
+import { JWTVerified } from "did-jwt";
 import * as R from "ramda";
 import { Component, Vue } from "vue-facing-decorator";
 import { Router } from "vue-router";
 
+import QuickNav from "@/components/QuickNav.vue";
+import EntityIcon from "@/components/EntityIcon.vue";
+import OfferDialog from "@/components/OfferDialog.vue";
 import { AppString, NotificationIface } from "@/constants/app";
 import { db, retrieveSettingsForActiveAccount } from "@/db/index";
 import { Contact } from "@/db/tables/contacts";
 import * as libsUtil from "@/libs/util";
-import QuickNav from "@/components/QuickNav.vue";
-import EntityIcon from "@/components/EntityIcon.vue";
-import OfferDialog from "@/components/OfferDialog.vue";
+import { decodeAndVerifyJwt } from "@/libs/crypto/vc/index";
 import { setVisibilityUtil } from "@/libs/endorserServer";
 
 @Component({
@@ -127,9 +130,27 @@ export default class ContactImportView extends Vue {
     this.apiServer = settings.apiServer || "";
 
     // Retrieve the imported contacts from the query parameter
-    const importedContacts =
-      ((this.$route as Router).query["contacts"] as string) || "[]";
-    this.contactsImporting = JSON.parse(importedContacts);
+    const importedContacts = (this.$route as Router).query[
+      "contacts"
+    ] as string;
+    if (importedContacts) {
+      await this.setContactsSelected(JSON.parse(importedContacts));
+    }
+
+    // match everything after /contact-import/ in the window.location.pathname
+    const jwt = window.location.pathname.match(
+      /\/contact-import\/(ey.+)$/,
+    )?.[1];
+    if (jwt) {
+      // decode the JWT
+      // eslint-disable-next-line prettier/prettier
+      const parsedJwt: Omit<JWTVerified, "didResolutionResult" | "signer" | "jwt"> = await decodeAndVerifyJwt(jwt);
+      await this.setContactsSelected(parsedJwt.payload.contacts as Contact[]);
+    }
+  }
+
+  async setContactsSelected(contacts: Array<Contact>) {
+    this.contactsImporting = contacts;
     this.contactsSelected = new Array(this.contactsImporting.length).fill(true);
 
     await db.open();
diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue
index d82bd6e54..75761cf27 100644
--- a/src/views/ContactsView.vue
+++ b/src/views/ContactsView.vue
@@ -323,7 +323,7 @@ import GiftedDialog from "@/components/GiftedDialog.vue";
 import OfferDialog from "@/components/OfferDialog.vue";
 import ContactNameDialog from "@/components/ContactNameDialog.vue";
 import TopMessage from "@/components/TopMessage.vue";
-import { AppString, NotificationIface } from "@/constants/app";
+import { APP_SERVER, AppString, NotificationIface } from "@/constants/app";
 import {
   db,
   retrieveSettingsForActiveAccount,
@@ -336,6 +336,7 @@ import { decodeEndorserJwt } from "@/libs/crypto/vc";
 import {
   CONTACT_CSV_HEADER,
   CONTACT_URL_PREFIX,
+  createEndorserJwtForDid,
   GiveSummaryRecord,
   getHeaders,
   isDid,
@@ -415,6 +416,7 @@ export default class ContactsView extends Vue {
     );
 
     // handle a contact sent via URL
+    // @deprecated: use /contact-import/:jwt with a JWT that has an array of contacts
     const importedContactJwt = (this.$route as RouteLocationNormalizedLoaded)
       .query["contactJwt"] as string;
     if (importedContactJwt) {
@@ -1247,7 +1249,7 @@ export default class ContactsView extends Vue {
     };
   }
 
-  private copySelectedContacts() {
+  private async copySelectedContacts() {
     if (this.contactsSelected.length === 0) {
       this.danger("You must select contacts to copy.");
       return;
@@ -1255,18 +1257,23 @@ export default class ContactsView extends Vue {
     const selectedContacts = this.contacts.filter((c) =>
       this.contactsSelected.includes(c.did),
     );
-    const message =
-      "To add contacts, paste this into the box on the 'Contacts' screen.\n\n" +
-      JSON.stringify(selectedContacts);
+    console.log(
+      "Array of selected contacts:",
+      JSON.stringify(selectedContacts),
+    );
+    const contactsJwt = await createEndorserJwtForDid(this.activeDid, {
+      contacts: selectedContacts,
+    });
+    const contactsJwtUrl = APP_SERVER + "/contact-import/" + contactsJwt;
     useClipboard()
-      .copy(message)
+      .copy(contactsJwtUrl)
       .then(() => {
         this.$notify(
           {
             group: "alert",
             type: "info",
             title: "Copied",
-            text: "Those contacts were copied to the clipboard. Have them paste it in the box on their 'Contacts' screen.",
+            text: "The link for those contacts is now in the clipboard.",
           },
           5000,
         );
diff --git a/src/views/GiftedDetailsView.vue b/src/views/GiftedDetailsView.vue
index 3248ff87f..465c5be59 100644
--- a/src/views/GiftedDetailsView.vue
+++ b/src/views/GiftedDetailsView.vue
@@ -114,7 +114,7 @@
             {{
               giverDid
                 ? "This was provided by " + giverName + "."
-                : "No individual gave."
+                : "No named individual gave."
             }}
           </label>
           <fa
diff --git a/src/views/IdentitySwitcherView.vue b/src/views/IdentitySwitcherView.vue
index 43922034f..5e4630e17 100644
--- a/src/views/IdentitySwitcherView.vue
+++ b/src/views/IdentitySwitcherView.vue
@@ -188,7 +188,7 @@ export default class IdentitySwitcherView extends Vue {
         group: "alert",
         type: "warning",
         title: "Cannot Delete",
-        text: "You cannot delete the active identity.",
+        text: "You cannot delete the active identity. Set to another identity or 'no identity' first.",
       },
       3000,
     );