# Get a contact card Returns the card Project Blue stored for the line and, read live, what Messages on the line currently holds. This is the source of truth after an `INDETERMINATE` set and the way to notice a card someone changed on the Mac by hand. `GET /get-contact-card` ### Query parameters - `lineId` (string (UUID v4), required) — The line to read. Use a lineId returned from GET /get-lines. ### The line object - `readiness` ("READY" | "OFFLINE" | "UNSUPPORTED_SERVER" | "PRIVATE_API_DISABLED" | "HELPER_DISCONNECTED" | "NOT_REGISTERED" | "LINE_NOT_ELIGIBLE", required) — Whether the line can take contact-card calls right now, checked live on every request. Anything other than READY also appears as an error_code on the write endpoints. - `serverVersion` (string | null, required) — BlueBubbles version the line is running. Contact cards need 2.2.0 or newer. - `name` (string | null, required) — The name Messages on the line currently holds, read live. null when the line could not be read. - `hasPhoto` (boolean | null, required) — Whether Messages on the line currently holds a photo. - `state` ("APPLIED" | "MISMATCH" | "NOT_SET" | "UNMANAGED" | "UNAVAILABLE", required) — How the live card compares with the stored one. APPLIED matches; MISMATCH means someone changed it on the Mac or a set did not finish; NOT_SET has no card anywhere; UNMANAGED has a card on the Mac that was never set through Project Blue; UNAVAILABLE means the line could not be read. When the live card matches the stored one, `appliedToLine` on the stored card is set to `true`; when it does not, it is cleared. Reading is always 200 — a line that cannot be reached reports `state: UNAVAILABLE` with the readiness reason rather than an error. **Request — cURL** ```bash curl -G https://api.tryprojectblue.com/get-contact-card \ -H "Authorization: Bearer YOUR_API_KEY" \ --data-urlencode "lineId=a3f8c2d1-b4e9-4f2a-c8d3-e1f0a2b3c4d5" ``` **Request — cURL · JavaScript** ```javascript // Run on your server. Keep your API key private. const url = new URL("https://api.tryprojectblue.com/get-contact-card"); url.searchParams.append("lineId", "a3f8c2d1-b4e9-4f2a-c8d3-e1f0a2b3c4d5"); const response = await fetch(url, { method: "GET", headers: { "Authorization": `Bearer ${process.env.PB_API_KEY}` } }); const data = await response.json(); if (!response.ok) throw new Error(JSON.stringify(data)); console.log(data); ``` **Request — cURL · Python** ```python import os import requests response = requests.request( "GET", "https://api.tryprojectblue.com/get-contact-card", headers={ "Authorization": "Bearer " + os.environ["PB_API_KEY"] }, params=[("lineId", "a3f8c2d1-b4e9-4f2a-c8d3-e1f0a2b3c4d5")], timeout=30, ) response.raise_for_status() print(response.json()) ``` **Response — 200 OK** ```json { "lineId": "a3f8c2d1-b4e9-4f2a-c8d3-e1f0a2b3c4d5", "contactCard": { "cardId": "cmm7p3x9a0001s6ryh2k4d8nq", "firstName": "Jamie", "lastName": "Rivera Realty", "imageUrl": "https://…/contact-poster-images/api/user-42/line-84/3f9c…e1.jpg", "hasPhoto": true, "appliedToLine": true, "updatedAt": "2026-10-13T17:04:12.118Z" }, "line": { "readiness": "READY", "serverVersion": "2.2.0", "name": "Jamie Rivera Realty", "hasPhoto": true, "state": "APPLIED" } } ``` **Response — 200 — line not ready** ```json { "lineId": "a3f8c2d1-b4e9-4f2a-c8d3-e1f0a2b3c4d5", "contactCard": null, "line": { "readiness": "UNSUPPORTED_SERVER", "serverVersion": "2.1.2", "name": null, "hasPhoto": null, "state": "UNAVAILABLE" } } ```