This document describes how to integrate Kakao Story APIs into your service with the Kakao SDK for Android. ("Android SDK").
To use Kakao Story, you need to add the v2-story
module that provides StoryApiClient
for Kakao Story and the v2-user
module for Kakao Login in the module-level build.gradle file. Refer to Add modules for more details.
To allow your app to launch the Kakao Story application, you must set a Custom URL Scheme in AndroidManifest.xml.
<activity
android:name=".${YOUR_ACTIVITY_NAME}"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!-- Set a scheme to launch the Kakao Story app in "kakao${YOUR_NATIVE_APP_KEY}://kakaolink" format. -->
<data android:host="kakaostory"
android:scheme="kakao${YOUR_NATIVE_APP_KEY}" />
</intent-filter>
</activity>
Permission | Prerequisite | Kakao Login | User consent |
---|---|---|---|
- | Register platforms Activate Kakao Login Manage consent items |
Required | - |
To check if the user who is currently logged in is using Kakao Story, call the isStoryUser()
method in the StoryApiClient
class.
// Checking if user is using Kakao Story
StoryApiClient.instance.isStoryUser { isStoryUser, error ->
if (error != null) {
Log.e(TAG, "Failed to validate Kakao Story user registration.", error)
}
else {
Log.i(TAG, "Whether to use Kakao Story: $isStoryUser")
}
}
var disposables = CompositeDisposable()
// Checking if user is using Kakao Story
StoryApiClient.rx.isStoryUser()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ isStoryUser ->
Log.i(TAG, "Whether to use Kakao Story: $isStoryUser")
}, { error ->
Log.e(TAG, "Failed to validate Kakao Story user registration.", error)
})
.addTo(disposables)
If true
is returned, the user is using Kakao Story. If the user is not using Kakao Story, false
is returned so that you can take action to prevent an error that occurs when calling Kakao Story APIs for the user.
Users can set their desired profile nicknames and images for Kakao Story, Kakao Talk, and Kakao Account respectively. Thus, profile information retrieved through this API is different from the ones obtained through the Retrieving user information or Retrieving Kakao Talk profile API.
Permission | Prerequisite | Kakao Login | User consent |
---|---|---|---|
- | Register platforms Activate Kakao Login Manage consent items |
Required | Required: Profile Info(nickname/profile image) Nickname Profile image KakaoStory Profile URL |
To retrieve the Kakao Story profile information of the user who is currently logged in, call the profile()
method in the StoryApiClient
class.
// Retrieving Kakao Story profile
StoryApiClient.instance.profile { profile, error ->
if (error != null) {
Log.e(TAG, "Failed to retrieve Kakao Story profile.", error)
}
else if (profile != null) {
Log.i(TAG, "Succeeded in retrieving Kakao Story profile." +
"\nNickname: ${profile.nickname}" +
"\nProfile Image: ${profile.thumbnailUrl}" +
"\nBirthday: ${profile.birthday}")
}
}
var disposables = CompositeDisposable()
// Retrieving Kakao Story profile
StoryApiClient.rx.profile()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ profile ->
Log.i(TAG, "Succeeded in retrieving Kakao Story profile." +
"\nNickname: ${profile.nickname}" +
"\nProfile Image: ${profile.thumbnailUrl}" +
"\nBirthday: ${profile.birthday}")
}, { error ->
Log.e(TAG, "Failed to retrieve Kakao Story profile.", error)
})
.addTo(disposables)
profile()
returns Kakao Story profile information through the StoryProfile
object.
The profile information retrieved through this API may be different from the profiles obtained through the Retrieving user information API or Retrieving Kakao Talk profile API because users can set their profile nicknames and images respectively for Kakao Story, Kakao Talk, and Kakao Account.
Name | Type | Description | Required |
---|---|---|---|
nickname | String |
Kakao Story nickname. Required user consent: Profile Info(nickname/profile image) or Nickname |
X |
profileImageUrl | String |
Kakao Story profile image URL. Required user consent: Profile Info(nickname/profile image) or Profile image |
X |
thumbnailUrl | String |
Kakao Story thumbnail profile image URL. Required user consent: Profile Info(nickname/profile image) or Profile image |
X |
bgImageUrl | String |
Kakao Story background image URL. Required user consent: Profile Info(nickname/profile image) or Profile image |
X |
permalink | String |
Kakao Story profile URL. Required user consent: KakaoStory profile URL NOTE: From June 1, 2021, only when a user consents to 'KakaoStory profile URL(story_permalink)', the Retrieving Kakao Story profile API returns 'permalink' in the response. For more details, see Notice. |
X |
birthday | String |
Birthday registered in Kakao Story in MMdd format. Required user consent: Birthday |
X |
birthdayType | BirthdayType |
- SOLAR : Solar birthday.- LUNAR : Lunar birthday.Required user consent: Birthday |
X |
To post a new story on Kakao Story of the user who is currently logged in, call one of the following methods depending on the story type — text, photo, or link. You also must pass arguments for the corresponding story type.
Type | Description | Method |
---|---|---|
Text | Story with text only. | postNote() |
Photo | Story with text and photos. | postPhoto() |
Link | Story with text and the information obtained by scrapping a web page. | postLink() |
Permission | Prerequisite | Kakao Login | User consent |
---|---|---|---|
- | Register platforms Activate Kakao Login Manage consent items |
Required | Required: Publish posts in KakaoStory |
To post a text story without any photo or web page URL, call the postNote()
method. You must also pass the content
parameter that contains the text you want to input.
Name | Type | Description | Required |
---|---|---|---|
content | String |
Text to be input in the story. Up to 2048 characters are allowed. |
O |
permission | Permission |
Audience for the story. - PUBLIC : Open to all.- FRIEND : Open to only friends.- ONLY_ME : Open to only me (Private). (Default: PUBLIC ) |
X |
enableShare | Boolean |
Whether to share the story when permission is set to FRIEND (Friends only).(Default: false ) |
X |
androidExecParam | Map<String, String> |
Parameter to be added to the URL to execute an Android app when selecting the [View on app] button from Kakao Story. | X |
iosExecParam | Map<String, String> |
Parameter to be added to the URL to execute an iOS app when selecting the [View on app] button from Kakao Story. | X |
androidMarketParam | Map<String, String> |
Parameter to be added to the execution URL when redirecting to an open market from Kakao Story. | X |
iosMarketParam | Map<String, String> |
Parameter to be added to the execution URL when redirecting to the App Store from Kakao Story. | X |
// Posting text story
val content = "Posting note from Kakao SDK Sample."
StoryApiClient.instance.postNote(content) { storyPostResult, error ->
if (error != null) {
Log.e(TAG, "Failed to post a story.", error)
}
else if (storyPostResult != null) {
Log.i(TAG, "Succeeded in posting a story. [${storyPostResult.id}]")
}
}
var disposables = CompositeDisposable()
// Posting text story
val content = "Posting note from Kakao SDK Sample."
StoryApiClient.rx.postNote(content)
.retryWhen(
// Request additional consent for InsufficientScope.
RxAuthOperations.instance.incrementalAuthorizationRequired(context)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ storyId ->
Log.i(TAG, "Succeeded in posting a story. [${storyId}]")
}, { error ->
Log.e(TAG, "Failed to post a story.", error)
})
.addTo(disposables)
Permission | Prerequisite | Kakao Login | User consent |
---|---|---|---|
- | Register platforms Activate Kakao Login Manage consent items |
Required | Required: Publish posts in KakaoStory |
To post a story with photos,
upload()
method by passing images
with List<String>
type to be posted in a story. upload()
returns the list of the uploaded image URLs.postPhoto()
method by passing images
that is obtained through upload()
.Name | Type | Description | Required |
---|---|---|---|
images | List<String> |
List of image URLs to be attached to a story. | O |
content | String |
Text to be input in the story. Up to 2048 characters are allowed. |
X |
permission | Permission |
Audience for the story. - PUBLIC : Open to all.- FRIEND : Open to only friends.- ONLY_ME : Open to only me (Private). (Default: PUBLIC ) |
X |
enableShare | Boolean |
Whether to share the story when permission is set to FRIEND (Friends only).(Default: false ) |
X |
androidExecParam | Map<String, String> |
Parameter to be added to the URL to execute an Android app when selecting the [View on app] button from Kakao Story. | X |
iosExecParam | Map<String, String> |
Parameter to be added to the URL to execute an iOS app when selecting the [View on app] button from Kakao Story. | X |
androidMarketParam | Map<String, String> |
Parameter to be added to the execution URL when redirecting to an open market from Kakao Story. | X |
iosMarketParam | Map<String, String> |
Parameter to be added to the execution URL when redirecting to the App Store from Kakao Story. | X |
The following code snippet shows the algorithm for uploading images and posting a photo story with the uploaded images.
// Posting photo story
// Image file to be uploaded
// This code snippet uses the image file added as a project resource. Use the imge files as your service needs.
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.sample1)
val file = File(context.cacheDir, "sample1.png")
val stream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
stream.close()
// Uploading image
StoryApiClient.instance.upload(listOf(file)) { uploadedPaths, error ->
if (error != null) {
Log.e(TAG, "Failed to upload image.", error)
} else {
Log.d(TAG, "Succeeded in uploading image. $uploadedPaths")
// Posting photo story
val images = uploadedPaths!!
val content = "Posting photo from Kakao SDK Sample."
StoryApiClient.instance.postPhoto(images, content) { storyPostResult, error ->
if (error != null) {
Log.e(TAG, "Failed to post a story.", error)
}
else if (storyPostResult != null) {
Log.i(TAG, "Succeeded in posting a story. [${storyPostResult.id}]")
}
}
}
}
var disposables = CompositeDisposable()
// Posting photo story
// Image file to be uploaded
// This code snippet uses the image file added as a project resource. Use the imge files as your service needs.
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.sample1)
val file = File(context.cacheDir, "sample1.png")
val stream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream)
stream.close()
// Uploading image
StoryApiClient.rx.upload(listOf(file))
.flatMap {
// Posting photo story
val images = it
val content = "Posting photo from Kakao SDK Sample."
StoryApiClient.rx.postPhoto(images, content)
}
.retryWhen(
// Request additional consent for InsufficientScope.
RxAuthOperations.instance.incrementalAuthorizationRequired(context)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ storyId ->
Log.i(TAG, "Succeeded in posting a story. [${storyId}]")
}, { error ->
Log.e(TAG, "Failed to post a story.", error)
})
.addTo(disposables)
Permission | Prerequisite | Kakao Login | User consent |
---|---|---|---|
- | Register platforms Activate Kakao Login Manage consent items |
Required | Required: Publish posts in KakaoStory |
To post a story with the information obtained by scrapping a web page,
linkInfo()
method by passing url
with a string type. linkInfo()
returns the LinkInfo
object that contains the web page information scraped based on the Open Graph Protocol. postLink()
method by passing LinkInfo
that is obtained through linkInfo()
.Name | Type | Description | Required |
---|---|---|---|
linkInfo | LinkInfo |
The web page information. | O |
content | String |
Text to be input in the story. Up to 2048 characters are allowed. |
X |
permission | Permission |
Audience for the story. - PUBLIC : Open to all.- FRIEND : Open to only friends.- ONLY_ME : Open to only me (Private). (Default: PUBLIC ) |
X |
enableShare | Boolean |
Whether to share the story when permission is set to FRIEND (Friends only).(Default: false ) |
X |
androidExecParam | Map<String, String> |
Parameter to be added to the URL to execute an Android app when selecting the [View on app] button from Kakao Story. | X |
iosExecParam | Map<String, String> |
Parameter to be added to the URL to execute an iOS app when selecting the [View on app] button from Kakao Story. | X |
androidMarketParam | Map<String, String> |
Parameter to be added to the execution URL when redirecting to an open market from Kakao Story. | X |
iosMarketParam | Map<String, String> |
Parameter to be added to the execution URL when redirecting to the App Store from Kakao Story. | X |
Name | Type | Description | Required |
---|---|---|---|
url | String |
URL of of the scraped web page. In the case of a shortened link, the resolved URL. |
X |
requestedUrl | String |
Original URL of the scraped web page. In the case of a shortened link, the original URL before resolving it. |
X |
host | String |
Site domain of the scraped web page. | X |
title | String |
Title of the scraped web page. | X |
images | List<String> |
List of representative images on the scraped web page. Up to three images are allowed. |
X |
description | String |
Description of the scraped web page. | X |
section | String |
Section information of the scraped web page. | X |
type | String |
Content type of the scraped web page such as video, music, book, article, profile, and website. | X |
The following code snippet shows the algorithm for scraping web page and posting a link story with the scraped web page information.
// Posting link story
// Create link info with the specified URL.
StoryApiClient.instance.linkInfo("https://www.kakaocorp.com") { linkInfoResult, error ->
if (error != null) {
Log.e(TAG, "Failed to create link info.", error)
} else {
Log.d(TAG, "Succeeded in creating link info. $linkInfoResult")
// Posting link story
val linkInfo = linkInfoResult!!
val content = "Posting link from Kakao SDK Sample."
StoryApiClient.instance.postLink(linkInfo, content) { storyPostResult, error ->
if (error != null) {
Log.e(TAG, "Failed to post a story.", error)
}
else if (storyPostResult != null) {
Log.i(TAG, "Succeeded in posting a story. [${storyPostResult.id}]")
}
}
}
}
var disposables = CompositeDisposable()
// Posting link story
// Create link info with the specified URL.
StoryApiClient.rx.linkInfo("https://www.kakaocorp.com")
.flatMap {
// Posting link story
val linkInfo = it
val content = "Posting link from Kakao SDK Sample."
StoryApiClient.rx.postLink(linkInfo, content)
}
.retryWhen(
// Request additional consent for InsufficientScope.
RxAuthOperations.instance.incrementalAuthorizationRequired(context)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ storyId ->
Log.i(TAG, "Succeeded in posting a story. [${storyId}]")
}, { error ->
Log.e(TAG, "Failed to post a story.", error)
})
.addTo(disposables)
The methods to post a story return the StoryPostResult
object.
Name | Type | Description |
---|---|---|
id | String |
ID of the uploaded story. |
Permission | Prerequisite | Kakao Login | User consent |
---|---|---|---|
- | Register platforms Activate Kakao Login Manage consent items |
Required | Required: Read access to KakaoStory posts |
To retrieve story information on Kakao Story of the user currently logged in, call the stories()
method in the StoryApiClient
class.
// Retrieving multiple stories
StoryApiClient.instance.stories { stories, error ->
if (error != null) {
Log.e(TAG, "Failed to retrieve Kakao Story.", error)
}
else {
Log.i(TAG, "Succeeded in retrieving Kakao Story. \n$stories")
}
}
var disposables = CompositeDisposable()
// Retrieving multiple stories
StoryApiClient.rx.stories()
.retryWhen(
// Request additional consent for InsufficientScope.
RxAuthOperations.instance.incrementalAuthorizationRequired(context)
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ stories ->
Log.i(TAG, "Succeeded in retrieving Kakao Story. \n$stories")
}, { error ->
Log.e(TAG, "Failed to retrieve Kakao Story.", error)
}).addTo(disposables)
Permission | Prerequisite | Kakao Login | User consent |
---|---|---|---|
- | Register platforms Activate Kakao Login Manage consent items |
Required | Required: Read access to KakaoStory posts |
To retrieve the detailed information of a specific story, call the story()
method by passing id
included in the Story
object. Then, you can get detailed information including comments and reactions.
Name | Type | Description | Required |
---|---|---|---|
id | String |
Story ID to be retrieved. | O |
// Retrieving specified story
// This code snippet shows the algorithm for retrieving all stories of the user
// to figure out story IDs and retrieving the first story by designating the story ID.
StoryApiClient.instance.stories { stories, error ->
if (error != null) {
Log.e(TAG, "Failed to retrieve Kakao Story.", error)
}
else if (stories != null) {
if (stories.isEmpty()) {
Log.e(TAG, "There is no story :(")
}
else {
// Story ID to be retrieved.
val storyId = stories.first().id
// Retrieving specified story
StoryApiClient.instance.story(storyId) { story, error ->
if (error != null) {
Log.e(TAG, "Failed to retrieve Kakao Story.", error)
}
else if (story != null) {
Log.i(TAG, "Succeeded in retrieving Kakao Story." +
"\nID: ${story.id}" +
"\nMedeia Type: ${story.mediaType}" +
"\nCreated at: ${story.createdAt}" +
"\nContent: ${story.content}")
}
}
}
}
var disposables = CompositeDisposable()
// Retrieving specified story
// This code snippet shows the algorithm for retrieving all stories of the user
// to figure out story IDs and retrieving the first story by designating the story ID.
StoryApiClient.rx.stories()
.retryWhen(
// Request additional consent for InsufficientScope.
RxAuthOperations.instance.incrementalAuthorizationRequired(context)
)
.flatMap { stories ->
if (stories.isNotEmpty()) {
// Story ID to be retrieved.
val storyId = stories.first().id
StoryApiClient.rx.story(storyId)
}
else {
Single.error(ClientError(ClientErrorCause.IllegalState, "No stories"))
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ story ->
Log.i(TAG, "Succeeded in retrieving Kakao Story." +
"\nID: ${story.id}" +
"\nMedeia Type: ${story.mediaType}" +
"\nCreated at: ${story.createdAt}" +
"\nContent: ${story.content}")
}, { error ->
Log.e(TAG, "Failed to retrieve Kakao Story.", error)
}).addTo(disposables)
story()
returns the Story
object, and stories()
returns a list of Story
objects that contains each story information. stories()
does not return comments
and likes
that are only returned through story()
.
Name | Type | Description | Required |
---|---|---|---|
id | String |
Story ID. | O |
url | String |
Story URL. | O |
content | String |
Story content. | O |
createdAt | Date |
The time when the story is posted in RFC3339 internet date/time format. | O |
mediaType | String |
Story type. One of photo (photo), note (text), notSupported (not supported). |
X |
media | List<StoryImage> |
URLs by image size. Only returned if mediaType is photo . |
X |
commentCount | Int |
Number of comments. | O |
likeCount | Int |
Number of moods that the user's friends left. | O |
permission | Permission |
Audience for the story. - PUBLIC : Open to all.- friend : Open to only friends.- onlyMe : Open to only me (Private). (Default: PUBLIC ) |
X |
comments | List<StoryComment> |
List of comments consisting of text (content) and writer (the user who left the comment).NOTE: Not returned if stories() is called. |
X |
likes | List<StoryLike> |
List of moods consisting of emotion (mood emoji) and actor (the user who left the mood).NOTE: Not returned if stories() is called. |
X |
Name | Type | Description | Required |
---|---|---|---|
large | String |
URL of the image with a size of 720x960 pixels. | X |
medium | String |
URL of the image with a size of 240x320 pixels. | X |
original | String |
URL of the original image. | X |
small | String |
URL of the image with a size of 160x213 pixels. | X |
xlarge | String |
URL of the image with a size of 1280x1706 pixels. | X |
Name | Type | Description | Required |
---|---|---|---|
text | String |
Comment content. | O |
writer | StoryActor |
User who left a comment. | O |
Name | Type | Description | Required |
---|---|---|---|
emotion | Emotion |
Mood left in a story. One of like , cool , happy , sad , cheerUp . |
O |
actor | StoryActor |
User who left the mood. | O |
Name | Type | Description | Required |
---|---|---|---|
displayName | String |
User's Kakao Story nickname. | O |
profileThumbnailUrl | String |
User's Kakao Story profile thumbnail URL. | X |
Permission | Prerequisite | Kakao Login | User consent |
---|---|---|---|
- | Register platforms Activate Kakao Login Manage consent items |
Required | Required: Read access to KakaoStory posts |
To delete a story on a user's Kakao Story, call the delete()
method in the StoryApiClient
class. You must pass id
to specify the story ID you want to delete.
Name | Type | Description | Required |
---|---|---|---|
id | String |
Story ID to be deleted. If you do not know the story ID, call the Retrieving my story API. |
O |
The following code snippet shows the algorithm for retrieving all stories of the user and deleting the first story.
// Deleting story
// This code snippet shows the algorithm for retrieving all stories of the user and deleting the first story.
StoryApiClient.instance.stories { stories, error ->
if (error != null) {
Log.e(TAG, "Failed to retrieve Kakao Story.", error)
}
else if (stories != null) {
if (stories.isEmpty()) {
Log.e(TAG, "There is no story :(")
}
else {
// Story ID to be deleted.
val storyId = stories.first().id
// Deleting story
StoryApiClient.instance.delete(storyId) { error ->
if (error != null) {
Log.e(TAG, "Failed to delete story.", error)
} else {
Log.i(TAG, "Succeeded in deleting story. [$storyId]")
}
}
}
}
}
var disposables = CompositeDisposable()
// Deleting story
// This code snippet shows the algorithm for retrieving all stories of the user and deleting the first story.
StoryApiClient.rx.stories()
.retryWhen(
// Request additional consent for InsufficientScope.
RxAuthOperations.instance.incrementalAuthorizationRequired(context)
)
.flatMapCompletable { stories ->
if (stories.isNotEmpty()) {
// Story ID to be deleted.
val storyId = stories.first().id
StoryApiClient.rx.delete(storyId)
}
else {
Completable.error(ClientError(ClientErrorCause.IllegalState, "There is no story :("))
}
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
Log.i(TAG, "Succeeded in deleting story.")
}, { error ->
Log.e(TAG, "Failed to delete story.", error)
}).addTo(disposables)