From
bafyreibupusaow3u5mg7pikgsaarvnfn5u2xuvizwofshi7c4hanob6pnu
indexed 2026-06-21 00:58 UTC
View this version
To
bafyreifujrh63pgqjy7ycyr3p3vifv2l6j4xgepxoayne2d73jktw3nc4i
indexed 2026-10-29 21:53 UTC
View this version
Compatibility
backward compatible Backward compatible. 10 non-breaking changes.
Non-breaking changes (10)
- AddedVertex AddedVertex { vertex_id: "dev.cocore.devicePair.start:input" }
- AddedVertex AddedVertex { vertex_id: "dev.cocore.devicePair.start:input.appDid" }
- AddedVertex AddedVertex { vertex_id: "dev.cocore.devicePair.start:input.appName" }
- AddedVertex AddedVertex { vertex_id: "dev.cocore.devicePair.start:input.keyName" }
- AddedVertex AddedVertex { vertex_id: "dev.cocore.devicePair.start:input.returnUrl" }
- AddedEdge AddedEdge { src: "dev.cocore.devicePair.start", tgt: "dev.cocore.devicePair.start:input", kind: "prop", name: Some("input") }
- AddedEdge AddedEdge { src: "dev.cocore.devicePair.start:input", tgt: "dev.cocore.devicePair.start:input.appDid", kind: "prop", name: Some("appDid") }
- AddedEdge AddedEdge { src: "dev.cocore.devicePair.start:input", tgt: "dev.cocore.devicePair.start:input.appName", kind: "prop", name: Some("appName") }
- AddedEdge AddedEdge { src: "dev.cocore.devicePair.start:input", tgt: "dev.cocore.devicePair.start:input.keyName", kind: "prop", name: Some("keyName") }
- AddedEdge AddedEdge { src: "dev.cocore.devicePair.start:input", tgt: "dev.cocore.devicePair.start:input.returnUrl", kind: "prop", name: Some("returnUrl") }
Migration guidance
Added
AddedVertex { vertex_id: "dev.cocore.devicePair.start:input" }AddedVertex { vertex_id: "dev.cocore.devicePair.start:input.appDid" }AddedVertex { vertex_id: "dev.cocore.devicePair.start:input.appName" }AddedVertex { vertex_id: "dev.cocore.devicePair.start:input.keyName" }AddedVertex { vertex_id: "dev.cocore.devicePair.start:input.returnUrl" }
Notes
- Non-breaking: AddedEdge { src: "dev.cocore.devicePair.start", tgt: "dev.cocore.devicePair.start:input", kind: "prop", name: Some("input") }
- Non-breaking: AddedEdge { src: "dev.cocore.devicePair.start:input", tgt: "dev.cocore.devicePair.start:input.appDid", kind: "prop", name: Some("appDid") }
- Non-breaking: AddedEdge { src: "dev.cocore.devicePair.start:input", tgt: "dev.cocore.devicePair.start:input.appName", kind: "prop", name: Some("appName") }
- Non-breaking: AddedEdge { src: "dev.cocore.devicePair.start:input", tgt: "dev.cocore.devicePair.start:input.keyName", kind: "prop", name: Some("keyName") }
- Non-breaking: AddedEdge { src: "dev.cocore.devicePair.start:input", tgt: "dev.cocore.devicePair.start:input.returnUrl", kind: "prop", name: Some("returnUrl") }
Changes
11
{22
"$type": "com.atproto.lexicon.schema",33
"defs": {44
"main": {5
"description": "Begin a device-pairing flow (OAuth 2.0 device-authorization style). Returns a `deviceId` plus a short human-typable `userCode` and the `verificationUri` where a signed-in user approves the pairing. The caller then polls `dev.cocore.devicePair.poll` until approval.",5
"description": "Begin a device-pairing flow (OAuth 2.0 device-authorization style). Returns a `deviceId` plus a short human-typable `userCode` and the `verificationUri` where a signed-in user approves the pairing. The caller then polls `dev.cocore.devicePair.poll` until approval. The optional input lets an application (rather than a provider machine) say who it is: `appName` is shown on the approve screen, `keyName` names the minted API key so the user can recognise and revoke it, and `returnUrl` is where the approve screen sends the browser afterwards. `returnUrl` is honoured only for hosts the deployment allowlists; anything else is dropped silently, never rejected.",6
"errors": [7
{8
"description": "Too many pairing attempts from this client; retry later.",9
"name": "RateLimited"10
},11
{12
"description": "`appDid` is not a DID, or has no `dev.cocore.app.registration` record (message begins `AppNotRegistered`).",13
"name": "InvalidRequest"14
}15
],16
"input": {17
"encoding": "application/json",18
"schema": {19
"properties": {20
"appDid": {21
"description": "The requesting application's DID. When present the AppView reads the app's `dev.cocore.app.registration` record: the approve screen shows that record's `name` (the `appName` field is ignored) and `returnUrl` is honoured only if it matches one of the record's `returnUrls` AND the return host proves ownership via `/.well-known/cocore-app.json`. A DID with no registration record is rejected with InvalidRequest (AppNotRegistered).",22
"format": "did",23
"type": "string"24
},25
"appName": {26
"description": "Human name of the requesting application, shown on the approve screen.",27
"maxLength": 40,28
"type": "string"29
},30
"keyName": {31
"description": "Name for the API key minted on approval.",32
"maxLength": 100,33
"type": "string"34
},35
"returnUrl": {36
"description": "Where to send the browser after approval. Must be https and on an allowlisted host.",37
"format": "uri",38
"maxLength": 2000,39
"type": "string"40
}41
},42
"type": "object"43
}44
},645
"output": {746
"encoding": "application/json",847
"schema": {948
"properties": {1049
"deviceId": {1150
"type": "string"1251
},1352
"expiresInSecs": {1453
"description": "Seconds until this pairing attempt expires.",1554
"type": "integer"1655
},1756
"pollIntervalSecs": {1857
"description": "Minimum seconds between poll calls.",1958
"type": "integer"2059
},2160
"userCode": {2261
"description": "Short code the user types at the verification URI.",2362
"type": "string"2463
},2564
"verificationUri": {2665
"format": "uri",2766
"type": "string"2867
}2968
},3069
"required": [3170
"deviceId",3271
"userCode",3372
"verificationUri",3473
"pollIntervalSecs",3574
"expiresInSecs"3675
],3776
"type": "object"3877
}3978
},4079
"type": "procedure"4180
}4281
},4382
"id": "dev.cocore.devicePair.start",4483
"lexicon": 14584
}