Profile service endpoints¶
- For general information see How to work with the profile service.
- Technical definitions of each API, sample code, test UI and Open API definitions to download you can find in the developer portal. -> developer.discover.swiss/apis
General headers¶
Header Name | Type | Description |
---|---|---|
Ocp-Apim-Subscription-Key | string | Appropriate subscription key you can get in the developer portal |
Accept-Timezone | string | Supply a TimeZone id property acquired from Infocenter /timezones route or IANA code in order to get data with desired time offset. The default is UTC. |
Content-Type | string | default: application/json;charset=UTF-8 Use a different charset if your application is not sending the data in utf-8. for example: application/json;charset=ISO-8859-1 |
General query params¶
This query params might be applied to the next endpoints: * GET /orders * GET /partnerdata * GET /tickets
Param Name | Type | Description |
---|---|---|
continuationToken | string | Continuation token used to get next page of data read about paging |
top | int | Number of next set of entities read about paging |
includeCount | bool | if true - includes total count of entities |
Token and helpers¶
creates tokens and main profile objects
url | verb | request parameters | response |
---|---|---|---|
/token | POST | grant_type max_refresh_lifetime refresh_token |
ProfileToken Response If susccessful this endpoint always retuns a new token to access the resulting profile. |
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJQcm9maWxlSWQiOiIyZDk5ZThlOS03ODJhLTQ4ZWEtODRiNi0zYzdhYTRkMjEyYzQiLCJJc0d1ZXN0IjoiVHJ1ZSIsIm5iZiI6MTU5MjIxNzEwNiwiZXhwIjoxNTkyMjIwNzA2LCJpYXQiOjE1OTIyMTcxMDZ9.4qFf7YYkovASjeiqgtFQTMa-HqmAf_Snnu97sVx3l3Y",
"tokenType": "Bearer",
"expiresIn": 1592220706,
"expiresInDate": "2020-06-15T11:31:46+00:00",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJSZWZyZXNoSWQiOiIyZDk5ZThlOS03ODJhLTQ4ZWEtODRiNi0zYzdhYTRkMjEyYzQiLCJuYmYiOjE1OTIyMTcxMDYsImV4cCI6MTU5MjMwMzUwNiwiaWF0IjoxNTkyMjE3MTA2fQ.z16y-IlVsIgkhMwk1BkOo_5FupBJT8axIPIhBX3NuE8"
}
Info
There can be several different valid tokens for the same profile with different expiration date.
Request parameters
Name | value |
---|---|
grant_type | see below |
max_refresh_lifetime | wished refresh token lifetime in days. |
refresh_token | the refresh token which was delivered within a past token reference |
grant_type¶
grant_type | description | remarks |
---|---|---|
guest | creates a new guest- Person object in the profile. | If there is a profile-ID present in the request header it will return a BadRequest-Error |
authorization_header | returns a new profile token to an existing profile based on the Authorization header or creates a new Person object in the profile, copies azure B2C data into it and returns a token for the profile. |
|
authorization_header + valid ProfileToken-header |
Adds the Azure B2C data to the existing profile. | If there is already a profile with the provided B2C ID, the 2 profiles are merged as described below (the old B2C profile survives) |
refresh_token | returns a new token response if the refresh token is valid |
max_refresh_lifetime¶
wished refresh token lifetime in days. This value should be set as low as possible. There is an upper limit set by discover.swiss: 450 days
Tip
Not in all scenarios the same lifetime of the refresh token makes sense. For client apps a longer lifetime is better so the user is still logged in even after several month of not using the App. But on web application this is a more serious security issue and the refresh token should only be valid for the duration of an expected user "session".
refresh_token¶
The refreshToken from one of tha past token request-responses.
Warning
If you are using guest profiles without authentication: Once the refresh token has expired you can not get a new one nor a new profile token.
Merge profiles A → B¶
If your application offers profile functionality "as a guest" without B2C authentication and then offers the guest to add/use a login for his account you must call the token endpoint in this manner
POST {profileServiceUrl}/token?grant_type=authorization_header
ProfileToken: yyy.yyyy.yyyy
Authorization: Bearer xxx.xxx.xxx
- A user is acting «as a guest» and therefore with a specific profile, let's call it A profileToken-header -> profile A
-
Then the user creates a B2C Account and A is extendend by the B2C id Authorization-header -> is not pointing to a profile yet -> no merge is necesssary
-
A user is acting «as a guest» and therefore with a specific profile, let's call it B profileToken-header -> profile B
- Then he logs in with an existing B2C account and now there would be 2 profiles A and B which would be connected with the same login Authorization-header -> profile A -> a merge of A and B is necssary (and performed automaticallay)
A gets merged into B → B survives
- orders and tickets: move all from A to B (all objects from A get the profileID of B)
- parties: move all from A to B (all objects from A get the profileID of B)
- PartnerData: move all from A to B (all objects from A get the profileID of B)
- Cards: move all from A to B (all objects from A get the profileID of B)
- Stays: move all from A to B (all objects from A get the profileID of B) But if there is partnerdata existing with the same name-property the object of B survives and the one from A gets deleted
- delete person A
Person / me¶
url | verb | request | response | description |
---|---|---|---|---|
/me | GET | - | Person | returns the person-data of the current profile |
update me | /me | PATCH | Person or some properties of it |
Person |
/me/profileimage | GET | - | string / base64 | Downloads the thumbnail of the media as a base64 encoded string (same like /media/{identifier}/thumbnail) |
/me/verify/email | PUT | - | - | resend the account verification email. Email will only be sent if IdentificationLevel is EmailNotVerified |
Note
When you create a profile it is empty, information should be updated by using PATCH request {profileUrl}/me
Info
There is no POST nor PUT to replace or create the compete object. The logic always gets the existing object and applies changes to it.
Manage profile picture¶
Picture can be added to profile through profile media endpoints
How to add profile picture¶
-
Upload picture with additionalType = 'profileImage' via upload endpoint ([POST]
/media
)Data expected in form-data request:
file="/C:/Users/User/Pictures/profile_image.jpg"
name="My profile image"
additionalType="profileImage"
-
Add identifier of media to person request as profilePicture parameter ([PATCH]
/me
){ ... // any person informations such as familyName, etc. "profileImage":"123c123-abc1-1ab2-a54d-9abc13abc001_ProfileImage" }
- To display picture use one of 3 endpoints to download image:
- [GET]
/media/{identifier}/download
- Downloads the media as a file in the correct content type (png, jpg, ...) - [GET]
/media/{identifier}/base64
- Downloads the media as a base64 encoded string - [GET]
/media/{identifier}/thumbnail
- Downloads the thumbnail as a base64 encoded string
Party / group of fellow travelers¶
Data is selected based on the profile token. If the profile is linked to a B2C account a valid, suitable authorizationtoken must be present as well.
url | verb | request | response | description |
---|---|---|---|---|
/parties | GET | - | Party[] | Gets all parties (groups) of the current user |
/parties/{identifier} | GET | identifier | Party | Gets one single party (group) |
/parties | POST | Party | Party | Creates party or returns BadRequest and validation messages |
/parties/{identifier} | PUT | Party | Party | Updates party or returns BadRequest and validation messages |
/parties/{identifier} | DELETE | - | - | remove party from data base |
/parties/{identifier}/invite | POST | PartyInvitationResponse | Add permission (ds_p_reference) to join the party and returns invite token | |
/parties/{identifier}/invite | DELETE | 204 - no content | Delete permission (ds_p_reference) to join the party | |
/parties/join | PUT | JoinPartyRequest | Party | Add current user as a member to the party referenced in invite token |
Partner data / destination specific profile¶
Data is selected based on the profile token and the subscription key. If the profile is linked to a B2C account a valid, suitable authorizationtoken must be present as well.
Partner data is linked to the partner's acronym. Access is checked by subscription key
Instead of an ID which is different for every profile a name (made of letters and numbers) is used to store different named-profiledata for each user. This allowsfor example to organize the data for each user /partnerdata/memberdata /partnerdata/preferences
url | verb | request | response | description |
---|---|---|---|---|
/partnerdata | GET | - | PartnersDataResponse<PartnerData> | gets all groups of the current user |
/partnerdata/{name} | GET | name | PartnerData | gets one single group |
/partnerdata | POST | PartnerData | PartnerData | or returns BadRequest and validation messages |
/partnerdata/{name} | PUT | PartnerData | PartnerData | or returns BadRequest and validation messages |
/partnerdata/{name} | PATCH | PartnerData | PartnerData | Never returns an 404 error. If object is not existed it will create new. Never deletes AdditionalProperty in it. It either updates object with same PropertyId or add new |
/partnerdata/{name} | DELETE | - | - | |
/partnerdata/{name}/share | PUT | - | ProfileDataShareResponse | Adds share permission (ds_p_share) so that it can be imported by another user. |
/partnerdata/{name}/share | DELETE | - | 204 no content | Delete share permission (ds_p_share) so that it cannot be imported anymore |
/import/{sharingIdentifier} | PUT | - | ProfileDataImportResponse | Import a shared profile data object into your profile |
Order¶
Data is selected based on the profile token and the subscription key. If the profile is linked to a B2C account a valid, suitable authorizationtoken must be present as well.
Orders are always read only for the client.
url | verb | request | response | description |
---|---|---|---|---|
Get all orders of a profile | /orders | GET | OrdersResponse<Order> | |
Get a single order | /orders/{orderNumber} | GET | orderNumber | Order |
Orderinfo download¶
Data is selected only by the orderToken which is passed to the e-mail flow. There is no authentication on this endpoint. This endpoint is alternate way to get full information about tickets instead of requesting all tickets by id. Endpoint can be used to get order with full ticket list including child tickets.
url | verb | request | response | description |
---|---|---|---|---|
/orderinfos/{orderToken} | GET | orderToken | OrderDownload |
orderDownload sample:
{
"orderNumber": "20-107699",
"orderDate": "2020-12-10T20:13:51.9800292+00:00",
"partnerAcronym": "discover.swiss",
"@id": "https://api.discover.swiss/test/profile/orders/20-107699",
"customer": {
"email": "ordetest@gmail.com",
"familyName": "Eggenberger",
"givenName": "Christian",
"gender": "Male",
"birthDate": "1967-12-07T00:00:00"
},
"orderStatus": "Fulfilled",
"priceCurrency": "CHF",
"totalAmount": 27.0,
"totalAmountCHF": 27.0,
"mailBodyToken": "https://discoverswistestbusiness.blob.core.windows.net/mailbodies/07dcc9fc-403b-42ca-8b59-6d15e5aa8844_20-107699.html",
"language": "de",
"ticket": [
{
"name": "SBB - ZVV Zürich Card 24 Stunden",
"product": {
"@id": "https://api.discover.swiss/test/info/products/SBB_zurichcard24",
"identifier": "SBB_zurichcard24"
},
"additionalType": "e-ticket",
"ticketNumber": "640374080",
"ticketTokenId": "256754788543",
"bookingNumber": "640374080",
"ticketToken": "https://discoverswistestbusiness.blob.core.windows.net/tickets/7c36341a-855a-4119-95b7-4b3ecff8eaa6_nr_256754788543.pdf",
"qrCodeToken": "https://discoverswistestbusiness.blob.core.windows.net/qrcodes/f26dd17b-ca36-454e-b65a-62adbaa9a743_nr_256754788543.png",
"htmlToken": "https://discoverswistestbusiness.blob.core.windows.net/htmls/f41ed5b1-034e-4db2-97c7-18fef39356e3_nr_256754788543.html",
"dateIssued": "2020-12-10T20:14:32.4031913+00:00",
"priceCurrency": "CHF",
"totalPrice": 27.0,
"underName": {
"givenName": "SBB",
"familyName": "Tester",
"birthDate": "1967-12-07T00:00:00"
},
"validFrom": "2020-12-29T09:00:00+00:00",
"validUntil": "2020-12-29T09:00:00+00:00"
}
]
}
Tickets¶
Data is selected based on the profile token and the subscription key. If the profile is linked to a B2C account a valid, suitable authorizationtoken must be present as well.
They are always read only for the client.
url | verb | request | response | description |
---|---|---|---|---|
/tickets | GET | scope= - all - currentAndFuture - current - past orderNumber(optional) |
TicketsResponse<Ticket> | "currentAndFuture" is the default scope and delivers all tickets which are now active and in the future. The list is ordered by ValidFrom asc in scope current and currentAndFuture, ordered by validUntil desc in all other cases. |
/tickets/{identifier} | GET | identifier | Ticket | |
/tickets/{identifier}/download | GET | identifier | Binary[] / File response | downloads the ticket as a file in the correct content type (PDF, jpg, ...) public transportation: QR code only - not full PDF |
/tickets/{identifier}/base64 | GET | identifier | string / base64 | Gets the content of the ticket as base64 encoded string. which is sometimes helpful to display in a client application. |
tickets/guestcard | PUT | id, provider= - ds - 4tix |
Ticket | Copy created by provider guestcard or ticket to profile |
Info
public transportation: the download contains the QR code only - not the full PDF. The full PDF is accessible through the url in the ticketToken property of the ticket object.
GuestCardRequest sample:
{
"id": "107699",
"provider": "4tix"
}
Business trail¶
url | verb | request | response | description |
---|---|---|---|---|
/businesstrail | POST | BusinessTrail | empty | Creates a new Business-trail entry or returns BadRequest and validation messages |
Terms¶
url | verb | request | response | description |
---|---|---|---|---|
/terms/{termCode} | GET | the code of the Term (s and conditions) | TermVersion | Get term consent state. returns the current version of the term and information if it was accepted/rejected already |
/terms | POST | AcceptTermVersionRequest | TermVersion | Accept/reject a termversion. Adds the term version to the Business Trail or returns BadRequest and validation messages. |
/anonymous/terms | POST | query-string: max_refresh_lifetime={ n } body: AcceptTermVersionRequest NO ACCESS TOKENS NEEDED |
ProfileToken Response | Accept/reject a termversion and create a guest account. This request combines the creation of a guest profile and a BusinessTrail entry and is intended to use to store cookie consents/dissent of a website without any kind of profile and authentication messages. |
Info
If the posts are executed several times there will be several entries, but that doesn't hurt. The existence of the terms get's checked.
Media¶
url | verb | request | response | description |
---|---|---|---|---|
/media | GET | ProfileMedia[] | Gets all media of the current user | |
/media/{identifier} | GET | identifier | ProfileMedia | Gets one single media |
/media/{identifier}/download | GET | identifier | Binary[] / File response | Downloads the media as a file in the correct content type (png, jpg, ...) |
/media/{identifier}/base64 | GET | identifier | string / base64 | Downloads the media as a base64 encoded string |
/media/{identifier}/thumbnail | GET | identifier | string / base64 | Downloads the thumbnail as a base64 encoded string |
/media | POST, PUT | multipart/form-data request ProfileMediaRequest |
ProfileMedia | POST and PUT are supported and currently only 1 additional type and 1 media object per type is supported there is a upload size limit 5Mb |
OpenAPI document¶
All methods and response models can be viewed in the Developer Portal. To generate an API client you can use the following URLs without user authentication but still with the Subscription Key in the Header (Ocp-Apim-Subscription-Key
) or as Query Parameter (subscription-key=<your-surbscription-key>
):