Generate PIN Set Token
curl --request POST \
--url https://api.example.com/v1/card/set-pin/token \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--header 'x-client-key: <x-client-key>' \
--data '
{
"redirectUrl": "<string>",
"isEmbedded": true,
"customCss": {
"customCss.backgroundColor": "<string>",
"customCss.textColor": "<string>",
"customCss.backgroundColorPrimary": "<string>",
"customCss.textColorPrimary": "<string>",
"customCss.buttonBorderRadius": 123,
"customCss.pinBorderRadius": 123,
"customCss.pinBorderColor": "<string>"
}
}
'import requests
url = "https://api.example.com/v1/card/set-pin/token"
payload = {
"redirectUrl": "<string>",
"isEmbedded": True,
"customCss": {
"customCss.backgroundColor": "<string>",
"customCss.textColor": "<string>",
"customCss.backgroundColorPrimary": "<string>",
"customCss.textColorPrimary": "<string>",
"customCss.buttonBorderRadius": 123,
"customCss.pinBorderRadius": 123,
"customCss.pinBorderColor": "<string>"
}
}
headers = {
"x-client-key": "<x-client-key>",
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-client-key': '<x-client-key>',
Authorization: '<authorization>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
redirectUrl: '<string>',
isEmbedded: true,
customCss: {
'customCss.backgroundColor': '<string>',
'customCss.textColor': '<string>',
'customCss.backgroundColorPrimary': '<string>',
'customCss.textColorPrimary': '<string>',
'customCss.buttonBorderRadius': 123,
'customCss.pinBorderRadius': 123,
'customCss.pinBorderColor': '<string>'
}
})
};
fetch('https://api.example.com/v1/card/set-pin/token', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/card/set-pin/token",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'redirectUrl' => '<string>',
'isEmbedded' => true,
'customCss' => [
'customCss.backgroundColor' => '<string>',
'customCss.textColor' => '<string>',
'customCss.backgroundColorPrimary' => '<string>',
'customCss.textColorPrimary' => '<string>',
'customCss.buttonBorderRadius' => 123,
'customCss.pinBorderRadius' => 123,
'customCss.pinBorderColor' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json",
"x-client-key: <x-client-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v1/card/set-pin/token"
payload := strings.NewReader("{\n \"redirectUrl\": \"<string>\",\n \"isEmbedded\": true,\n \"customCss\": {\n \"customCss.backgroundColor\": \"<string>\",\n \"customCss.textColor\": \"<string>\",\n \"customCss.backgroundColorPrimary\": \"<string>\",\n \"customCss.textColorPrimary\": \"<string>\",\n \"customCss.buttonBorderRadius\": 123,\n \"customCss.pinBorderRadius\": 123,\n \"customCss.pinBorderColor\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-client-key", "<x-client-key>")
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/v1/card/set-pin/token")
.header("x-client-key", "<x-client-key>")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"redirectUrl\": \"<string>\",\n \"isEmbedded\": true,\n \"customCss\": {\n \"customCss.backgroundColor\": \"<string>\",\n \"customCss.textColor\": \"<string>\",\n \"customCss.backgroundColorPrimary\": \"<string>\",\n \"customCss.textColorPrimary\": \"<string>\",\n \"customCss.buttonBorderRadius\": 123,\n \"customCss.pinBorderRadius\": 123,\n \"customCss.pinBorderColor\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/card/set-pin/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-client-key"] = '<x-client-key>'
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"redirectUrl\": \"<string>\",\n \"isEmbedded\": true,\n \"customCss\": {\n \"customCss.backgroundColor\": \"<string>\",\n \"customCss.textColor\": \"<string>\",\n \"customCss.backgroundColorPrimary\": \"<string>\",\n \"customCss.textColorPrimary\": \"<string>\",\n \"customCss.buttonBorderRadius\": 123,\n \"customCss.pinBorderRadius\": 123,\n \"customCss.pinBorderColor\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"token": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb",
"hostedPageUrl": "https://cards.baanx.com/pin-direct/set?token=100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
}
Card
Generate PIN Set Token
Generate a secure token for setting or changing the card PIN through a PCI-compliant hosted interface
POST
/
v1
/
card
/
set-pin
/
token
Generate PIN Set Token
curl --request POST \
--url https://api.example.com/v1/card/set-pin/token \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--header 'x-client-key: <x-client-key>' \
--data '
{
"redirectUrl": "<string>",
"isEmbedded": true,
"customCss": {
"customCss.backgroundColor": "<string>",
"customCss.textColor": "<string>",
"customCss.backgroundColorPrimary": "<string>",
"customCss.textColorPrimary": "<string>",
"customCss.buttonBorderRadius": 123,
"customCss.pinBorderRadius": 123,
"customCss.pinBorderColor": "<string>"
}
}
'import requests
url = "https://api.example.com/v1/card/set-pin/token"
payload = {
"redirectUrl": "<string>",
"isEmbedded": True,
"customCss": {
"customCss.backgroundColor": "<string>",
"customCss.textColor": "<string>",
"customCss.backgroundColorPrimary": "<string>",
"customCss.textColorPrimary": "<string>",
"customCss.buttonBorderRadius": 123,
"customCss.pinBorderRadius": 123,
"customCss.pinBorderColor": "<string>"
}
}
headers = {
"x-client-key": "<x-client-key>",
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'x-client-key': '<x-client-key>',
Authorization: '<authorization>',
'Content-Type': 'application/json'
},
body: JSON.stringify({
redirectUrl: '<string>',
isEmbedded: true,
customCss: {
'customCss.backgroundColor': '<string>',
'customCss.textColor': '<string>',
'customCss.backgroundColorPrimary': '<string>',
'customCss.textColorPrimary': '<string>',
'customCss.buttonBorderRadius': 123,
'customCss.pinBorderRadius': 123,
'customCss.pinBorderColor': '<string>'
}
})
};
fetch('https://api.example.com/v1/card/set-pin/token', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/card/set-pin/token",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'redirectUrl' => '<string>',
'isEmbedded' => true,
'customCss' => [
'customCss.backgroundColor' => '<string>',
'customCss.textColor' => '<string>',
'customCss.backgroundColorPrimary' => '<string>',
'customCss.textColorPrimary' => '<string>',
'customCss.buttonBorderRadius' => 123,
'customCss.pinBorderRadius' => 123,
'customCss.pinBorderColor' => '<string>'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json",
"x-client-key: <x-client-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v1/card/set-pin/token"
payload := strings.NewReader("{\n \"redirectUrl\": \"<string>\",\n \"isEmbedded\": true,\n \"customCss\": {\n \"customCss.backgroundColor\": \"<string>\",\n \"customCss.textColor\": \"<string>\",\n \"customCss.backgroundColorPrimary\": \"<string>\",\n \"customCss.textColorPrimary\": \"<string>\",\n \"customCss.buttonBorderRadius\": 123,\n \"customCss.pinBorderRadius\": 123,\n \"customCss.pinBorderColor\": \"<string>\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-client-key", "<x-client-key>")
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/v1/card/set-pin/token")
.header("x-client-key", "<x-client-key>")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"redirectUrl\": \"<string>\",\n \"isEmbedded\": true,\n \"customCss\": {\n \"customCss.backgroundColor\": \"<string>\",\n \"customCss.textColor\": \"<string>\",\n \"customCss.backgroundColorPrimary\": \"<string>\",\n \"customCss.textColorPrimary\": \"<string>\",\n \"customCss.buttonBorderRadius\": 123,\n \"customCss.pinBorderRadius\": 123,\n \"customCss.pinBorderColor\": \"<string>\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/card/set-pin/token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-client-key"] = '<x-client-key>'
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"redirectUrl\": \"<string>\",\n \"isEmbedded\": true,\n \"customCss\": {\n \"customCss.backgroundColor\": \"<string>\",\n \"customCss.textColor\": \"<string>\",\n \"customCss.backgroundColorPrimary\": \"<string>\",\n \"customCss.textColorPrimary\": \"<string>\",\n \"customCss.buttonBorderRadius\": 123,\n \"customCss.pinBorderRadius\": 123,\n \"customCss.pinBorderColor\": \"<string>\"\n }\n}"
response = http.request(request)
puts response.read_body{
"token": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb",
"hostedPageUrl": "https://cards.baanx.com/pin-direct/set?token=100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
}
Overview
Generates a time-limited secure token that allows users to set or change their card PIN through a PCI-compliant hosted interface. The hosted page includes built-in validation and confirmation flows to ensure the PIN is set correctly and securely.PCI ComplianceThis endpoint maintains PCI compliance by handling PIN creation entirely within secure hosted environments. Your application never handles or stores PIN values.
Authentication
This endpoint requires authentication via Bearer token:Authorization: Bearer YOUR_ACCESS_TOKEN
Request
Headers
string
required
Your public API client key
boolean
default:false
Set to
true to route requests to the US backend environmentstring
required
Bearer token for authentication
Body
string
URL to redirect to after the user completes or cancels PIN setupOnly used when
isEmbedded=falseExample: https://yourapp.com/dashboardboolean
default:false
Controls the completion/dismissal behavior:
false- Redirects toredirectUrlwhen user completes or cancelstrue- Fires apostMessageevent with typeabortfor iframe integration
object
Customize the visual appearance of the hosted PIN setup page
Show customCss properties
Show customCss properties
string
default:"#EFEFEF"
Main background colorExample:
#F9FAFB, #E5E7EBstring
default:"#000000"
Text color against main backgroundImportant: Avoid using the same hex as
backgroundColorExample: #000000, #111827string
default:"#000000"
Primary background color for buttons and input areasExample:
#000000, #1F2937string
default:"#FFFFFF"
Text color for buttons and input areasImportant: Avoid using the same hex as
backgroundColorPrimaryExample: #FFFFFF, #F3F4F6number
default:8
Button border radius in pixelsExample:
4, 8, 12number
default:4
PIN input container border radius in pixelsExample:
2, 4, 8string
Border color for PIN input containerDefault: Same as
backgroundColorPrimaryExample: #3B82F6, #10B981Request Example
curl -X POST https://dev.api.baanx.com/v1/card/set-pin/token \
-H "x-client-key: YOUR_CLIENT_KEY" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"redirectUrl": "https://yourapp.com/dashboard",
"isEmbedded": false,
"customCss": {
"backgroundColor": "#EFEFEF",
"textColor": "#000000",
"backgroundColorPrimary": "#000000",
"textColorPrimary": "#FFFFFF",
"buttonBorderRadius": 8,
"pinBorderRadius": 4,
"pinBorderColor": "#FF00FF"
}
}'
const response = await fetch('https://dev.api.baanx.com/v1/card/set-pin/token', {
method: 'POST',
headers: {
'x-client-key': 'YOUR_CLIENT_KEY',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
redirectUrl: 'https://yourapp.com/dashboard',
isEmbedded: false,
customCss: {
backgroundColor: '#EFEFEF',
textColor: '#000000',
backgroundColorPrimary: '#000000',
textColorPrimary: '#FFFFFF',
buttonBorderRadius: 8,
pinBorderRadius: 4
}
})
});
const data = await response.json();
console.log('Hosted page URL:', data.hostedPageUrl);
import requests
url = "https://dev.api.baanx.com/v1/card/set-pin/token"
headers = {
"x-client-key": "YOUR_CLIENT_KEY",
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"Content-Type": "application/json"
}
payload = {
"redirectUrl": "https://yourapp.com/dashboard",
"isEmbedded": False,
"customCss": {
"backgroundColor": "#EFEFEF",
"textColor": "#000000",
"backgroundColorPrimary": "#000000",
"textColorPrimary": "#FFFFFF",
"buttonBorderRadius": 8,
"pinBorderRadius": 4
}
}
response = requests.post(url, headers=headers, json=payload)
print(response.json())
interface SetPinTokenRequest {
redirectUrl?: string;
isEmbedded?: boolean;
customCss?: {
backgroundColor?: string;
textColor?: string;
backgroundColorPrimary?: string;
textColorPrimary?: string;
buttonBorderRadius?: number;
pinBorderRadius?: number;
pinBorderColor?: string;
};
}
interface SetPinTokenResponse {
token: string;
hostedPageUrl: string;
}
const generateSetPinToken = async (
config: SetPinTokenRequest
): Promise<SetPinTokenResponse> => {
const response = await fetch('https://dev.api.baanx.com/v1/card/set-pin/token', {
method: 'POST',
headers: {
'x-client-key': 'YOUR_CLIENT_KEY',
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
};
Response
Success Response
string
Secure, time-limited token (UUID format)Lifetime: ~10 minutesUsage: Single-use token that becomes invalid after access
string
Full URL to the hosted PIN setup pageUsage: Redirect users or embed in iframeFormat:
<HOST>/pin-direct/set?token={token}{
"token": "100a99cf-f4d3-4fa1-9be9-2e9828b20ebb",
"hostedPageUrl": "https://cards.baanx.com/pin-direct/set?token=100a99cf-f4d3-4fa1-9be9-2e9828b20ebb"
}
Error Responses
{
"message": "Not authenticated"
}
{
"message": "Not authorized"
}
{
"message": "Card not found"
}
{
"message": "Invalid client key"
}
{
"message": "Missing client key"
}
{
"message": "Internal server error"
}
Hosted Page Features
The hosted PIN setup page includes:PIN Entry
Secure input fields for entering 4-digit PIN
Confirmation
Two-step entry to prevent typos
Validation
Real-time validation of PIN format and strength
Masked Input
PIN digits are masked for privacy during entry
Integration Methods
- Full Page Redirect
- Iframe Embedding
Redirect the user to set their PIN on a dedicated page.User Flow:
const { hostedPageUrl } = await generateSetPinToken({
redirectUrl: 'https://yourapp.com/success',
isEmbedded: false
});
window.location.href = hostedPageUrl;
- User redirected to hosted page
- Enters new PIN twice for confirmation
- Submits PIN
- Redirected to
redirectUrlon success
Embed the PIN setup within your application.User Flow:
const { hostedPageUrl } = await generateSetPinToken({
isEmbedded: true
});
const iframe = document.createElement('iframe');
iframe.src = hostedPageUrl;
iframe.style.width = '100%';
iframe.style.height = '500px';
window.addEventListener('message', (event) => {
if (event.data.type === 'abort') {
iframe.remove();
}
});
document.getElementById('pin-container').appendChild(iframe);
- Iframe loads within your app
- User enters and confirms PIN
postMessageevent fires on completion or cancellation- Your app handles the event appropriately
Use Case Examples
First-Time PIN Setup
async function setupCardPIN() {
try {
const { hostedPageUrl } = await generateSetPinToken({
redirectUrl: `${window.location.origin}/card-activated`,
isEmbedded: false
});
showInfo('You will now set your card PIN');
window.location.href = hostedPageUrl;
} catch (error) {
console.error('Failed to initiate PIN setup:', error);
showError('Unable to set PIN. Please try again.');
}
}
Change Existing PIN
async function changePIN() {
const confirmed = await showConfirmDialog({
title: 'Change PIN?',
message: 'You will be able to set a new 4-digit PIN for your card.',
confirmText: 'Change PIN',
cancelText: 'Cancel'
});
if (confirmed) {
try {
const { hostedPageUrl } = await generateSetPinToken({
redirectUrl: window.location.href,
isEmbedded: false
});
window.location.href = hostedPageUrl;
} catch (error) {
console.error('Failed to change PIN:', error);
showError('Unable to change PIN. Please try again.');
}
}
}
Modal PIN Setup
function PINSetup() {
const [showModal, setShowModal] = useState(false);
const [iframeUrl, setIframeUrl] = useState('');
const handleSetPIN = async () => {
try {
const { hostedPageUrl } = await generateSetPinToken({
isEmbedded: true,
customCss: {
backgroundColor: '#F9FAFB',
textColor: '#111827',
backgroundColorPrimary: '#3B82F6',
textColorPrimary: '#FFFFFF',
buttonBorderRadius: 8,
pinBorderRadius: 4
}
});
setIframeUrl(hostedPageUrl);
setShowModal(true);
} catch (error) {
console.error('Failed to load PIN setup:', error);
}
};
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
if (event.data.type === 'abort') {
setShowModal(false);
setIframeUrl('');
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, []);
return (
<>
<button onClick={handleSetPIN}>Set Card PIN</button>
{showModal && (
<div className="modal">
<iframe
src={iframeUrl}
title="Set Card PIN"
style={{ width: '100%', height: '500px', border: 'none' }}
/>
</div>
)}
</>
);
}
Customization Examples
Modern Dark Theme
{
"customCss": {
"backgroundColor": "#1F2937",
"textColor": "#F9FAFB",
"backgroundColorPrimary": "#3B82F6",
"textColorPrimary": "#FFFFFF",
"buttonBorderRadius": 12,
"pinBorderRadius": 8,
"pinBorderColor": "#60A5FA"
}
}
Light Theme
{
"customCss": {
"backgroundColor": "#FFFFFF",
"textColor": "#111827",
"backgroundColorPrimary": "#10B981",
"textColorPrimary": "#FFFFFF",
"buttonBorderRadius": 8,
"pinBorderRadius": 4
}
}
Brand Colors
{
"customCss": {
"backgroundColor": "#FEF3C7",
"textColor": "#78350F",
"backgroundColorPrimary": "#F59E0B",
"textColorPrimary": "#FFFFFF",
"buttonBorderRadius": 10,
"pinBorderRadius": 6,
"pinBorderColor": "#D97706"
}
}
PIN Requirements
Standard PIN Format
- Must be exactly 4 digits
- Only numeric characters (0-9)
- No letters or special characters
- Cannot be all the same digit (e.g., 1111, 2222)
Security Best Practices
Token Security
- Tokens expire after ~10 minutes
- Single-use tokens become invalid after first access
- Generate new tokens for each PIN setup attempt
- Never store, cache, or log tokens
PCI ComplianceUsing this endpoint ensures PCI compliance as PIN data is created and stored entirely within secure, PCI-compliant systems. Your application never handles PIN values.
Secure CommunicationAll communication with the hosted page occurs over HTTPS with strong encryption. The PIN is never exposed in logs, analytics, or network traffic accessible to your application.
Common Use Cases
Onboarding Flow
async function cardActivationFlow() {
await orderCard();
let card = await getCardStatus();
while (card.status !== 'ACTIVE') {
await delay(2000);
card = await getCardStatus();
}
showSuccess('Card activated! Now set your PIN.');
const { hostedPageUrl } = await generateSetPinToken({
redirectUrl: `${window.location.origin}/dashboard`,
isEmbedded: false
});
window.location.href = hostedPageUrl;
}
Security Settings
function SecuritySettings() {
return (
<div>
<h2>Card Security</h2>
<button onClick={changePIN}>Change PIN</button>
<button onClick={viewPIN}>View Current PIN</button>
<button onClick={freezeCard}>Freeze Card</button>
</div>
);
}
Error Handling
async function safelySetPIN() {
try {
const card = await getCardStatus();
if (card.status !== 'ACTIVE') {
throw new Error('Card must be active to set PIN');
}
const { hostedPageUrl } = await generateSetPinToken({
redirectUrl: window.location.href,
isEmbedded: false
});
window.location.href = hostedPageUrl;
} catch (error) {
if (error.response?.status === 404) {
showError('No card found. Please order a card first.');
} else if (error.response?.status === 401) {
showError('Session expired. Please log in again.');
} else {
showError('Failed to set PIN. Please try again.');
}
}
}
Related Endpoints
POST /v1/card/pin/token- Generate token to view current PINPOST /v1/card/details/token- Generate token to view card detailsGET /v1/card/status- Check card status before PIN setupPOST /v1/card/order- Order a new card
Was this page helpful?
⌘I