···425425426426### Documenting Scope Requirements
427427428428-Use the `#[RequiresScope]` attribute to document which OAuth scopes your extension methods require. This helps with documentation and enables scope checking in authenticated mode:
428428+Use the `#[ScopedEndpoint]` attribute to document which OAuth scopes your extension methods require. This helps with documentation and enables scope checking in authenticated mode:
429429430430```php
431431-use SocialDept\AtpClient\Attributes\RequiresScope;
431431+use SocialDept\AtpClient\Attributes\ScopedEndpoint;
432432use SocialDept\AtpClient\Client\Requests\Request;
433433use SocialDept\AtpClient\Enums\Scope;
434434435435class BskyMetricsClient extends Request
436436{
437437- #[RequiresScope(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getTimeline')]
437437+ #[ScopedEndpoint(Scope::TransitionGeneric, granular: 'rpc:app.bsky.feed.getTimeline')]
438438 public function getTimelineMetrics(): array
439439 {
440440 $timeline = $this->atp->bsky->feed->getTimeline();
441441 // Process and return metrics...
442442 }
443443444444- // Methods without #[RequiresScope] work in both public and authenticated modes
444444+ // Methods without #[ScopedEndpoint] work in both public and authenticated modes
445445 public function getPublicPostMetrics(string $uri): array
446446 {
447447 $thread = $this->atp->bsky->feed->getPostThread($uri);
···450450}
451451```
452452453453-Methods with `#[RequiresScope]` indicate they require authentication, while methods without it can work in public mode. See [scopes.md](scopes.md) for full documentation on scope handling.
453453+Methods with `#[ScopedEndpoint]` indicate they require authentication, while methods without it can work in public mode. See [scopes.md](scopes.md) for full documentation on scope handling.
454454455455## Available Domains
456456
+17-17
docs/scopes.md
···2323Scope::identity('handle') // Identity attribute access
2424```
25252626-### RequiresScope Attribute
2626+### ScopedEndpoint Attribute
27272828```php
2929-use SocialDept\AtpClient\Attributes\RequiresScope;
2929+use SocialDept\AtpClient\Attributes\ScopedEndpoint;
3030use SocialDept\AtpClient\Enums\Scope;
31313232-#[RequiresScope(Scope::TransitionGeneric)]
3232+#[ScopedEndpoint(Scope::TransitionGeneric)]
3333public function getTimeline(): GetTimelineResponse
3434{
3535 // Method implementation
···7373'identity:handle' // Manage handle
7474```
75757676-## The RequiresScope Attribute
7676+## The ScopedEndpoint Attribute
77777878-The `#[RequiresScope]` attribute documents and optionally enforces scope requirements on methods.
7878+The `#[ScopedEndpoint]` attribute documents and optionally enforces scope requirements on methods.
79798080### Basic Usage
8181···84848585namespace App\Atp;
86868787-use SocialDept\AtpClient\Attributes\RequiresScope;
8787+use SocialDept\AtpClient\Attributes\ScopedEndpoint;
8888use SocialDept\AtpClient\Client\Requests\Request;
8989use SocialDept\AtpClient\Enums\Scope;
90909191class CustomClient extends Request
9292{
9393- #[RequiresScope(Scope::TransitionGeneric)]
9393+ #[ScopedEndpoint(Scope::TransitionGeneric)]
9494 public function getTimeline(): array
9595 {
9696 return $this->atp->client->get('app.bsky.feed.getTimeline')->json();
···103103Document the future granular scope that will replace the transition scope:
104104105105```php
106106-#[RequiresScope(
106106+#[ScopedEndpoint(
107107 Scope::TransitionGeneric,
108108 granular: 'rpc:app.bsky.feed.getTimeline'
109109)]
···118118Add a human-readable description for documentation:
119119120120```php
121121-#[RequiresScope(
121121+#[ScopedEndpoint(
122122 Scope::TransitionGeneric,
123123 granular: 'rpc:app.bsky.feed.getTimeline',
124124 description: 'Access to the user\'s home timeline'
···134134When a method requires multiple scopes, all must be present:
135135136136```php
137137-#[RequiresScope([Scope::TransitionGeneric, Scope::TransitionEmail])]
137137+#[ScopedEndpoint([Scope::TransitionGeneric, Scope::TransitionEmail])]
138138public function getEmailPreferences(): array
139139{
140140 // Requires BOTH scopes
···146146Use multiple attributes for alternative scope requirements:
147147148148```php
149149-#[RequiresScope(Scope::Atproto)]
150150-#[RequiresScope(Scope::TransitionGeneric)]
149149+#[ScopedEndpoint(Scope::Atproto)]
150150+#[ScopedEndpoint(Scope::TransitionGeneric)]
151151public function getProfile(string $actor): ProfileViewDetailed
152152{
153153 // Either scope satisfies the requirement
···298298$client->bsky->feed->getTimeline(); // Requires transition:generic scope
299299```
300300301301-Methods that work in public mode typically don't have `#[RequiresScope]` attributes, while authenticated-only methods do.
301301+Methods that work in public mode typically don't have `#[ScopedEndpoint]` attributes, while authenticated-only methods do.
302302303303## Exception Handling
304304···339339340340### 1. Document All Scope Requirements
341341342342-Always add `#[RequiresScope]` to methods that require authentication:
342342+Always add `#[ScopedEndpoint]` to methods that require authentication:
343343344344```php
345345-#[RequiresScope(
345345+#[ScopedEndpoint(
346346 Scope::TransitionGeneric,
347347 granular: 'rpc:app.bsky.feed.getTimeline',
348348 description: 'Fetches the authenticated user\'s home timeline'
···356356357357```php
358358// Good
359359-#[RequiresScope(Scope::TransitionGeneric)]
359359+#[ScopedEndpoint(Scope::TransitionGeneric)]
360360361361// Avoid
362362-#[RequiresScope('transition:generic')]
362362+#[ScopedEndpoint('transition:generic')]
363363```
364364365365### 3. Request Minimal Scopes