Career portfolios
A Career Portfolio is a person’s résumé on United Pet Club: a headline and About, skills from the UPC skill catalogue with a comfort level, the breeds they work with, roles, education, awards and work samples. It exists for person profiles only. Its owner decides, section by section, who can see it, so the same request returns more or less depending on who asks.
All reads on this page are anonymous and return the platform envelope { data, resultCode } with
camelCase fields. The {locale} segment is a BCP-47 code such as en-US (see
Localized names); names are localized with an en-US fallback. Ids are UUIDs.
Base path through the gateway: /api/profile/v1/{locale}/Profile.
| Method | Path | Returns |
|---|---|---|
GET | /profiles/{profileId}/career-portfolio | The portfolio as the caller may see it. 404 when the profile is not an active person profile. |
GET | /profiles/{profileId}/portfolio-items | The work samples. On a person profile this is the portfolio’s Work section: a caller who may not see it gets an empty list. |
GET | /skills/catalog | The skill catalogue: active categories with their skills. |
GET | /career-portfolio/lookups | Names for the codes used in the portfolio: employment types, degree types, award kinds, work settings and job titles. |
GET | /profiles/{profileId}/awards | A profile’s awards on their own, for a person, business or place profile. On a person profile it follows the Awards section’s audience. Only public statuses for anyone but the profile itself. |
Find a person’s profileId from their handle with GET …/Profile/profiles/by-handle/{handle}.
What you receive
Section titled “What you receive”A call without a member’s session receives the public view: the sections the person shared with everyone. Treat that as the most you can show; never try to infer what is hidden.
viewerisPUBLICfor such a call.- A section the caller may not see comes back as an empty list plus an entry in
lockedSections({ code, audience },audiencebeingMEMBERSorNETWORK). Show a “visible to members” note there, or nothing. A section the person keeps to themselves is simply absent, with no entry. - When the person made the whole portfolio private, the response is
{ profileId, isPublic: false, isHidden: true }with every list empty and no headline. Show nothing but a “this portfolio is private” note. isOpenToWorkis always present, so you can show an “Open to work” badge even when the hiring details are not shared (hiringis thennull).workVisiblesays whether the Work section (theportfolio-itemsread) is visible to the caller.
Section codes: ABOUT, SKILLS, BREEDS, EXPERIENCE, EDUCATION, AWARDS, WORK, HIRING.
{ "data": { "profileId": "0199…", "isPublic": true, "isHidden": false, "viewer": "PUBLIC", "isOpenToWork": true, "headline": "Groomer for doodles and anxious dogs", "about": "Calm, unhurried grooms. I specialize in nervous dogs and doodle trims.", "sectionAudiences": null, "lockedSections": [{ "code": "EXPERIENCE", "audience": "MEMBERS" }], "skills": [ { "id": "0199…", "skillCode": "GRM_SCISSORING", "name": "Hand scissoring", "categoryCode": "GROOMING_TECHNIQUE", "categoryName": "Grooming techniques", "categoryIconSvg": "<svg …>", "skillIconSvg": "<svg …>", "experienceLevel": 5, "sinceYear": 2016, "lastUsedYear": null, "years": 10, "isHighlighted": true, "description": null, "displayOrder": 1 } ], "breeds": [ { "id": "0199…", "speciesCode": "CAT", "speciesName": "Cat", "breedCode": null, "breedName": null, "experienceLevel": 3, "sinceYear": null, "note": null, "isHighlighted": false, "displayOrder": 2 } ], "experience": [], "education": [ { "id": "0199…", "qualification": "Grooming diploma", "degreeTypeCode": "CERTIFICATE", "institution": { "profileId": null, "handle": null, "name": "Lakeshore Grooming Academy", "isLinked": false, "isVerified": false }, "startDate": "2014-01-01", "endDate": "2014-08-01", "isCompleted": true, "description": null } ], "awards": [], "workVisible": true, "hiring": null }, "resultCode": "…"}The example is a public view of a test portfolio with ids and icons shortened. Codes such as CERTIFICATE
are named by the lookups read.
Fields
Section titled “Fields”| Field | Meaning |
|---|---|
experienceLevel | Comfort level 1–5: 1 Learning, 2 With guidance, 3 Works independently, 4 Experienced, 5 Can teach it. The same scale for skills and breeds. |
sinceYear, lastUsedYear, years | Years the person gave; years = (lastUsedYear or the current year) − sinceYear, never negative, null without sinceYear. |
isHighlighted | Shown at the top: up to 6 skills, up to 8 breeds (as tiles). |
breedCode: null | The row covers every breed of the species (“All breeds”). |
speciesCode | The species code used across the platform (DOG, CAT, HORSE, …). |
employer / institution / issuer | A reference: either a UPC profile (isLinked: true, with its current name, handle and isVerified; link to it) or plain text (isLinked: false, name only; do not link). A reference to a profile that has since closed comes back as plain text. |
jobTitle, jobTitleCode | jobTitle is the display text (localized when the title is from the catalogue); jobTitleCode is set only for catalogue titles. |
startDate, endDate, … | ISO yyyy-MM-dd, always the first of a month. isCurrent: true roles have no endDate. |
sectionAudiences | Always null unless the caller is the owner. |
awards[].statusCode, awards[].sourceCode | Since 2026-10-09. See Award statuses. |
Award statuses
Section titled “Award statuses”Since 2026-10-09 every award carries a statusCode and a sourceCode, in the portfolio’s awards list and in
GET /profiles/{profileId}/awards.
sourceCode | Meaning |
|---|---|
SELF | Recorded by the profile itself, naming who gave it (issuer: a UPC profile or plain text). |
ISSUER | Given by the business profile in issuer. |
statusCode | Meaning | In a public read |
|---|---|---|
CONFIRMED | The issuer confirmed it, or the profile accepted an award a business gave it. Show a “Confirmed” mark. | Yes |
SELF_REPORTED | Recorded by the profile; the issuer has not confirmed it (a plain-text issuer can’t). Label it “Not confirmed”. | Yes |
PENDING | Waiting for the issuer to confirm, or for the profile to accept a business’s award. | No |
DECLINED | The issuer declined it, or the profile declined a business’s award. | No |
WITHDRAWN | The business took back an award it gave. | No |
Anyone but the profile itself only ever receives CONFIRMED and SELF_REPORTED awards; never treat a missing award as
proof of anything. Awards recorded before 2026-10-09 are all SELF / SELF_REPORTED. An award whose issuer profile has
since closed while it was pending comes back as SELF_REPORTED.
"awards": [ { "id": "0199…", "title": "Best in Show", "awardKindCode": "COMPETITION", "issuer": { "profileId": "0199…", "handle": "groomexpo", "name": "Groom Expo", "isLinked": true, "isVerified": false }, "awardedDate": "2025-03-01", "description": null, "statusCode": "CONFIRMED", "sourceCode": "SELF" }]The example is illustrative, with ids shortened. GET /profiles/{profileId}/awards returns a flatter row:
{ id, awardKindCode, title, issuerName, issuerProfileId, awardedDate, description, statusCode, sourceCode }.
Managing awards (acting as the profile)
Section titled “Managing awards (acting as the profile)”These routes are on the same base path (/api/profile/v1/{locale}/Profile). Unlike the reads above they are not
anonymous: the caller must act as {profileId} with permission to edit it. A call without a signed-in identity is
401; a caller who may not edit the profile is 403. {profileId} is the side you act for: the profile the award is
on (recipient) or the profile that gave or is named as the giver (issuer).
| Method | Path | Side | Does |
|---|---|---|---|
POST | /profiles/{profileId}/awards | recipient | Record an award: { title, awardKindCode, issuerProfileId?, issuerName?, awardedDate?, description? } (an issuer id or a name, at least one). A linked issuer is asked to confirm (PENDING); a plain-text one saves SELF_REPORTED. 201 |
PUT | /profiles/{profileId}/awards/{awardId} | recipient | Edit a recorded award (same body). Changing a CONFIRMED award asks the issuer again. 403 on an award a business gave; 409 on one the issuer declined |
DELETE | /profiles/{profileId}/awards/{awardId} | recipient | Remove any award from the profile |
GET | /profiles/{profileId}/awards/received | recipient | Every award on the profile, every status, except business awards it declined |
POST | /profiles/{profileId}/awards/{awardId}/request-confirmation | recipient | Ask the linked issuer of a SELF_REPORTED award to confirm it → PENDING |
POST | /profiles/{profileId}/awards/{awardId}/accept | recipient | Accept a PENDING award a business gave → CONFIRMED |
POST | /profiles/{profileId}/awards/{awardId}/decline | recipient | Decline it → DECLINED |
GET | /profiles/{profileId}/award-confirmations | issuer | Awards others recorded naming this profile, waiting for its answer |
POST | /profiles/{profileId}/award-confirmations/{awardId}/confirm | issuer | → CONFIRMED |
POST | /profiles/{profileId}/award-confirmations/{awardId}/decline | issuer | Optional body { noteText } (at most 300 characters, shown to the member) → DECLINED |
GET | /profiles/{profileId}/given-awards | issuer | Awards this business gave, every status |
POST | /profiles/{profileId}/given-awards | issuer | Give an award: { recipientProfileId, awardKindCode, titleLocaleDefault, descriptionLocaleDefault?, awardedDate? } → PENDING. Business profiles only. 201 |
PUT | /profiles/{profileId}/given-awards/{awardId} | issuer | Edit it (same body without the recipient); changing an accepted award asks the recipient again |
DELETE | /profiles/{profileId}/given-awards/{awardId} | issuer | Withdraw it: a pending award is deleted, an accepted one becomes WITHDRAWN |
Every write except DELETE …/awards/{awardId} returns the award’s id and its status now: { "id": "0199…", "statusCode": "PENDING" } (a withdrawal answers WITHDRAWN whether the row was deleted or kept). The three GET
lists return awards with id, profileId, awardKindCode, awardKindName, titleLocaleDefault, descriptionLocaleDefault, awardedDate, sourceCode, statusCode, requestedAtUtc, decidedAtUtc, decisionNoteText, recipient, issuer (references
like issuer above, issuer null for a plain-text giver), issuerNameLocaleDefault and canEdit.
Rules and refusals:
- Awards are recorded on person, business and place profiles; only a business profile gives them, to any active profile but itself.
- Two profiles run by the same owners never ask each other: recording saves
SELF_REPORTED, asking is400, giving is403. - A profile holds at most 30 awards (declined and withdrawn ones don’t count), and a business can have at most
3 pending awards for the same recipient; past either limit the write is
403. - An action on an award that is no longer in the status it needs (someone answered first) is
409: read the list again. 400for a missing title, a title over 200 or a description over 1,000 characters, or an unknownawardKindCode(names from/career-portfolio/lookups).404for an award that isn’t on (or doesn’t name){profileId}.
Skill catalogue
Section titled “Skill catalogue”GET /skills/catalog returns { categories: [ { code, name, iconSvg, displayOrder, professionalKindCodes, skills: [ { code, name, speciesCode, iconSvg } ] } ] }: 24 categories and 493 skills, localized into every
platform locale. speciesCode is set only for species-specific skills; professionalKindCodes lists the
professions for which a category comes first (empty = everyone). iconSvg is a self-contained square SVG
string; render it as an image (for example a data:image/svg+xml URI), not as inline markup.
Skill codes are permanent: a code never changes meaning. A retired skill drops out of the catalogue, but
existing portfolio rows keep their skillCode and name.
Lookups
Section titled “Lookups”GET /career-portfolio/lookups returns { employmentTypes, degreeTypes, awardKinds, workSettings, jobTitles }.
Each item is { code, name }; job titles add professionalKindCode. Work settings are SALON, MOBILE,
IN_HOME, CLINIC, KENNEL, FARM and REMOTE. Cache lookups and the catalogue for an hour or more; they
change only with platform releases.
Caching
Section titled “Caching”The public view of a portfolio is safe to cache briefly (a few minutes): it is the same for every anonymous caller. Do not cache a response fetched with a member’s session under the same key as the public view.
Not available
Section titled “Not available”Editing a portfolio is not part of the partner surface: the writes require the person’s own session acting
as their profile (the award routes above included). Organization profiles have no Career Portfolio; use
portfolio-items for their work samples and awards for their awards.